【问题标题】:Execute settimeout early提前执行 settimeout
【发布时间】:2012-02-24 04:41:00
【问题描述】:

我正在使用去抖动在使用 settimeout 超时后执行事件。我遇到的问题是其他 javascript 事件期望这些事件同步发生。由于它们现在在超时后执行,我希望能够通过其他 javascript 事件过早地触发它们(因此那些需要它们的事件不会失败)。

任何人,如果我这样做:

timeout = setTimeout(function() { alert('hi'); }, 10000);

,我希望在 10 秒过去之前发生,我该怎么做?

如有必要,该解决方案可以涉及 jquery。谢谢!

编辑: 是否可以通过访问超时对象来做​​到这一点?

【问题讨论】:

  • “其他 javascript 事件期望这些事件同步发生”......这听起来像是一个有问题的设计

标签: javascript jquery settimeout


【解决方案1】:

所以,如果你做任何你正在延迟它自己的功能的东西:

function sayHi() {
    alert('hi');
}

您可以使用超时和常规函数调用:

var timeout = setTimeout(sayHi, 10000); // say hi after 10 seconds

或者在超时到期之前调用它,只需在需要时调用该函数:

sayHi();

我在正确的轨道上吗?如果您需要取消超时,请在您的 timeout 变量上调用 clearTimeout()

if (timeout)
    clearTimeout(timeout);

【讨论】:

  • 抱歉不清楚。我很好奇是否可以仅使用 setTimeout 的返回值。
  • @GoldenNewby:setTimeout() 的返回值只是一个 ID,因此您可以跟踪它(并取消它)。否则没有太多目的。
  • @Cory +1 但我不得不不同意Not much purpose for it otherwise。如果你有一个注册的 ID,我觉得你可以有一个 triggerTimeoutgetTimeout 甚至 updateTimeout 方法以及 set 和 clear ——遗憾的是它们不存在,但它们可以写作为一个 polyfill,包含 setTimeoutclearTimeout 的定制覆盖。
【解决方案2】:

您无法使用标准 setTimeout 对其进行跟踪,但 Javascript 允许您根据需要增强功能。

例如,您可以拥有自己的增强型setTimeout

var _setTimeout = window.setTimeout;
var timeouts = [];
window.setTimeout = function(fn, ms) {
    var id = _setTimeout(fn, ms);
    timeouts[id] = fn;
    return id;
};

window.premature = function(id) {
    var fn = timeouts[id];
    if (fn) {
        clearTimeout(id);
        if (fn instanceof String) {
            eval(fn);
        } else {
            fn()
        }
    }
};


function printDate(str) {
    $("body").append("<p>" + str + ". " + new Date() + "</p>");

}

$(function() {
    var id1 = setTimeout(function() { printDate("id1"); }, 10000);
    var id2 = setTimeout(function() { printDate("id2"); }, 10000);
    printDate("first one");
    // just to demonstrate that the string version works too
    setTimeout("window.premature(" + id1 +")", 5000);
});

您可以在jsFiddle看到它的实际应用

请注意,这个简单的 hack 并没有考虑在超时确实发生时清除使用的 id,但这只是为了表明如果你真的需要的话,你可以在 Javascript 中做这种事情。

【讨论】:

  • 真正的setTimeout有两个以上的参数,毫秒参数之后的任何参数都传给回调。示例:setTimeout(alert, 2000, 'this is passed to alert two seconds later');
【解决方案3】:

polyfill 解决方案

这是我从以前的项目中更新的一些 javascript,它现在通过触发和更新方法进行了扩展;它类似于 Jan Wikholm 的解决方案 (+1) — 但更完整一点,考虑到清除、传递参数和防止 eval(如果需要):

(function(keep){

  /// a few things to remember
  keep.setTimeout = window.setTimeout;
  keep.clearTimeout = window.clearTimeout;
  keep.TO = function(){};
  keep.list = {};
  keep.settings = {
    eval: false /// set this to true if you wish to support string timeouts
  };

  /**
   * Quick check function to prevent eval
   */
  keep.checkParam = function( param ){
    if ( !keep.settings.eval && typeof param == 'string' ) {
      throw new Error('setTimeout blocked evaluation of string, ' + 
        'use a function instead.');
      return false;
    }
    else if ( param ) {
      return true;
    }
  };

  /**
   * Simple function constructor to avoid trapping unwanted references
   */
  keep.makeFunction = function(data){
    return function(args){
      /// copy our args array
      args = data.args.slice();
      /// do we allow eval?
      if ( keep.settings.eval ) {
        /// if so, reuse setTimeout for it's abilities
        args[0] = data.param; /// use the original param
        args[1] = 0;          /// trigger immediately
        keep.setTimeout.apply( window, args );
      }
      // more secure, assume dealing with function -- not string
      else if ( keep.checkParam( data.param ) && data.param.apply ) {
        data.param.apply( window, args.slice(2) );
      }
      else {
        throw new Error('unsupported param for setTimeout' + 
          ' i.e. non-function used.');
      }
      /// clear our storage of this tid
      window.clearTimeout( data.tid );
    };
  };

  /**
   * Sets timeouts just like you would expect
   */
  window.setTimeout = function( param, timeout ){
    if ( keep.checkParam( param ) ) {
      var tid, data;
      /// support passing a timeout object as param
      if ( param instanceof keep.TO ) {
        data = param;
        data.args[1] = data.timeout;
      }
      else {
        /// create an object to store the timeout info
        data = new keep.TO();
        data.func = keep.makeFunction(data);
        data.param = param;
        data.timeout = timeout;
        data.args = Array.prototype.slice.call(arguments,0);
        data.args[0] = data.func;
      }
      data.tid = keep.setTimeout.apply( window, data.args );
      keep.list[data.tid] = data;
      /// enhance the returned number to support .clear, .trigger and .update
      tid = new Number(data.tid);
      tid.clear = window.clearTimeout;
      tid.trigger = window.triggerTimeout;
      tid.update = window.updateTimeout;
      return tid;
    }
  };

  /**
   * Clearing timeouts since 2013
   */
  window.clearTimeout = function( tid ){
    if ( this instanceof Number ) {
      tid = 0 + this;
    }
    var obj;
    if ( (obj = window.getTimeout(tid)) ) {
      delete keep.list[tid];
      keep.clearTimeout.call(window, tid);
    }
  };

  /**
   * Returns the internal timeout storage object
   */
  window.getTimeout = function( tid ){
    var obj;
    if ( (obj = keep.list[tid]) ) {
      return obj;
    }
  };

  /**
   * Clears and fires a timeout before it's outed time
   */    
  window.triggerTimeout = function( tid ){
    if ( this instanceof Number ) {
      tid = 0 + this;
    }
    var obj;
    if ( (obj = window.getTimeout(tid)) ) {
      window.clearTimeout(tid);
      obj.func.call(window);
    }
    else {
      throw new Error('No Timeout found to trigger for ID '+ tid);
    }
  };

  /**
   * Clears and recreates an existing timeout, returns a new timeout id.
   */
  window.updateTimeout = function( tid, timeout ){
    if ( this instanceof Number ) {
      if ( arguments.length == 1 ) {
        timeout = tid;
      }
      tid = 0 + this;
    }
    var obj;
    if ( (obj = window.getTimeout(tid)) ) {
      obj.timeout = timeout;
      window.clearTimeout(tid);
      return window.setTimeout(obj);
    }
    else {
      throw new Error('No Timeout found to update for ID ' + tid);
    }
  };

  /**
   * Utility function to tidy up
   */
  window.clearAllTimeouts = function(){
    for ( var i in keep.list ) {
      window.clearTimeout(i);
    };
  };

  /// Tidy up
  window.onunload = (function(previous){
    return function(){
      window.clearAllTimeouts();
      keep.list = {};
      previous && previous.call(window);
    };
  }(window.onunload));

})({});

包括

只需将上面的内容放在一个js文件中,并使用普通的脚本标签包含到你的页面中,代码不需要以任何方式调用:

<script src="timeouts.js"></script>

用法

显然这应该像普通的setTimeout 调用一样使用,但是您现在有额外的方法应该提供更大的灵活性。

var tid = setTimeout( function(){ alert('OK Computer') }, 2000 );

例如,您可以取消原始并强制超时触发更早:

setTimeout( function(){ triggerTimeout( tid ); }, 500 );

或者,您可以更新超时(确保我们记住新返回的 tid)

setTimeout( function(){ tid = updateTimeout( tid, 5000 ); }, 500 );

你也可以照常做:

setTimeout( function(){ clearTimeout( tid ); }, 1000 );

也可以通过tid 本身访问这些方法中的每一个:

setTimeout( function(){ tid.trigger(); }, 1000 );
setTimeout( function(){ tid.update( 5000 ); }, 1000 );
setTimeout( function(){ tid.clear(); }, 1000 );

默认情况下,此代码禁止将setTimeout 与字符串参数一起使用,主要是因为传递函数而不是字符串是一种更好的编码风格。要更改此设置,您可以将以下设置切换为 true:

keep.settings = {
  eval: true
};

但不建议这样做。

禁用 eval 还有一个额外的好处,即代码将使用正常的函数调用来触发超时,即.apply()。这意味着无论您使用什么浏览器,您都可以通过 setTimeout 将参数传递给超时函数——这通常不是您可以依赖的跨浏览器。例如:

setTimeout( function(a){ alert(a) }, 2000, 'Hello World' );

【讨论】:

  • 太棒了,这正是我正在寻找的信息,太好了,我不需要自己写。谢谢:D
【解决方案4】:

把函数放出来,给它起个名字:

function handler(){ 
    alert('hi'); 
}

timeout = setTimeout(handler, 10000);  

那你可以在其他地方调用handler();

【讨论】:

    【解决方案5】:

    使用clearTimeout 并重新安排更早的时间。

    【讨论】:

      猜你喜欢
      • 2020-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-11
      相关资源
      最近更新 更多