Выполнить settimeout рано - PullRequest
       2

Выполнить settimeout рано

8 голосов
/ 24 февраля 2012

Я использую debouncing для выполнения событий после тайм-аута, используя settimeout.Проблема, с которой я столкнулся, состоит в том, что другие события javascript ожидают, что эти события происходят синхронно.Так как они теперь выполняются после истечения времени ожидания, я хотел бы иметь возможность преждевременно инициировать их с помощью других событий javascript (поэтому те события, которые требуют их, не потерпят неудачу).

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

, и я хочу, чтобы это произошло раньше, чем через 10 секунд, как я могу это сделать?

Решение может включать jquery, если это необходимо.Спасибо!

Редактировать: Возможно ли это сделать, просто имея доступ к объекту тайм-аута?

Ответы [ 5 ]

6 голосов
/ 24 февраля 2012

Вы не можете отследить его с помощью стандартного 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

Обратите внимание, что этот простой взлом не учитывает очистку использованных идентификаторов, когда тайм-ауты действительно происходят, а просто показывает, что вы можете делать такие вещи в Javascript, если вам это действительно нужно.

6 голосов
/ 24 февраля 2012

Итак, если вы делаете то, что задерживаете свою собственную функцию:

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

Вы можете использовать тайм-аут и обычный вызов функции:

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

Или вызвать его до истечения времени ожидания, просто вызывайте функцию всякий раз, когда вам нужно:

sayHi();

Я на правильном пути? Если вам нужно отменить время ожидания, позвоните clearTimeout() в вашей переменной timeout.

if (timeout)
    clearTimeout(timeout);
4 голосов
/ 06 сентября 2013

polyfill solution

Вот некоторый javascript, который я обновил из предыдущего проекта, теперь он расширен методами триггера и обновления;оно похоже на решение Яна Викхольма (+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));

})({});

include

Просто поместите вышеупомянутое в js-файл и включите в свою страницу, используя обычный тег сценария, код не нужно вызывать каким-либо образом:

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

использование

Очевидно, что это следует использовать точно так же, как обычный вызов setTimeout, однако теперь у вас есть дополнительные методы, которые должны обеспечить большую гибкость.

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

Например, вы можете отменить оригинал и заставить тайм-аут срабатывать раньше:

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

Или вы можете обновить тайм-аут (убедитесь, что мы помним новый возвращенный тид) :

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().Это означает, что независимо от того, какой браузер вы используете, вы можете передавать аргументы в функцию timeout через setTimeout - обычно это не то, на что вы можете положиться в кросс-браузере.например:

setTimeout( function(a){ alert(a) }, 2000, 'Hello World' );
1 голос
/ 24 февраля 2012

Просто отключите функцию и дайте ей имя:

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

timeout = setTimeout(handler, 10000);  

, затем вы можете вызвать ее в других местах с помощью handler();

0 голосов
/ 24 февраля 2012

Используйте clearTimeout и перенесите на более ранний срок.

...