简体   繁体   中英

Javascript setTimeout with 0 delay issue

I noticed a strange behavior: if I have a series of tasks and wish to defer their execution, then I can use a setTimeout with 0 delay for each of them. (see http://javascript.info/tutorial/events-and-timing-depth#the-settimeout-func-0-trick )

Everything works perfectly: the tasks are queued and executed as soon as possible.

But ... if the invocation of the various setTimeout , then I found that sometimes (rarely happens!) is not executed in the . ,那么我发现有时(很少发生!)执行Why?

Nobody ever promised they would be fired in the "correct" order (the tasks with the same timeout will be executed in the order they are set to time out). setTimeout only guarantees that:

  • each timeout is executed exactly once (unless the page dies in the meantime)
  • each timeout is executed no sooner than when it is supposed to.

There is no word about execution order. In fact, even if the implementor tried to preserve order (even as a side-effect), most likely there is not enough time resolution to provide a unique sort order to all tasks, and a binary heap (which may well be used here) does not preserve insertion order of equal keys).

If you want to preserve the order of your deferred tasks, you should only enqueue one when the previous one is done.

This should work:

var defer = (function(){
  //wrapped in IIFE to provide a scope for deferreds and wrap
  var running = false;
  var deferreds = [];
  function wrap(func){
    return function(){
      func();
      var next = deferreds.shift();
      if(next){
        setTimeout(wrap(next),0);
      }else{
        running = false;
      }
    }
  }
  return function(func){
    if(running){
      deferreds.push(func);
    }else{
      setTimeout(wrap(func),0);
      running = true;
    }
  }
})()

Demo: http://jsfiddle.net/x2QuB/1/

You can consider using jquery deferreds ( or some other implementation of deferreds), which can handle this pattern very elegantly.

The important point to note is that the deferred done callbacks are executed in the order in which they are added.

 var createCountFn  = function(val){  
    return function(){ 
        alert(val)
    };
}

 // tasks 
var f1 = createCountFn(1),
    f2 = createCountFn('2nd'),
    f3 = createCountFn(3);

 var dfd = $.Deferred();
 dfd.done(f1).done(f2).done(f3);

 dfd.resolve(); 

demo

The HTML5 draft specification states that the setTimeout method can be run asynchronously (implying that the order that the callbacks will be executed may not be preserved), which could be what your browser is doing.

The setTimeout() method must run the following steps:

...

6. Return handle, and then continue running this algorithm asynchronously.

7. If the method context is a Window object, wait until the Document associated with the method context has been fully active for a further timeout milliseconds (not necessarily consecutively).

In any case one could workaround this issue by doing something similar to this:

function inOrderTimeout(/* func1[, func2, func3, ...funcN], timeout */)
{   var timer; // for timeout later
    var args = arguments; // allow parent function arguments to be accessed by nested functions
    var numToRun = args.length - 1; // number of functions passed
    if (numToRun < 1) return; // damm, nothing to run
    var currentFunc = 0; // index counter
    var timeout = args[numToRun]; // timeout should be straight after the last function argument
    (function caller(func, timeout) // name so that recursion is possible
    {   if (currentFunc > numToRun - 1)
        {   // last one, let's finish off
            clearTimeout(timer);
            return;
        }
        timer = setTimeout(function () 
        {   func(); // calls the current function
            ++currentFunc; // sets the next function to be called
            caller(args[currentFunc], timeout);
        }, Math.floor(timeout));
    }(args[currentFunc], timeout)); // pass in the timeout and the first function to run
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM