簡體   English   中英

Javascript setTimeout具有0延遲問題

[英]Javascript setTimeout with 0 delay issue

我注意到了一個奇怪的行為:如果我有一系列任務並希望推遲執行,那么我可以為每個任務使用setTimeout並設置0延遲。 (請參閱http://javascript.info/tutorial/events-and-timing-depth#the-settimeout-func-0-trick

一切運行正常:將任務排入隊列並盡快執行。

但是...如果各種setTimeout的調用非常接近 ,那么我發現有時(很少發生!)執行順序正確 為什么?

沒有人曾承諾過他們會以“正確”的順序被解雇(具有相同超時的任務將按照設置為超時的順序執行)。 setTimeout僅保證:

  • 每個超時僅執行一次(除非頁面在此期間死亡)
  • 每次超時都不會早於預期的時間執行。

沒有關於執行順序的消息。 實際上,即使實現者嘗試保留順序(甚至是副作用),也很可能沒有足夠的時間解析來為所有任務提供唯一的排序順序,並且二進制堆(很可能在這里使用)不保留等號的插入順序)。

如果要保留延遲任務的順序,則僅在上一個任務完成后才入隊。

這應該工作:

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;
    }
  }
})()

演示: http//jsfiddle.net/x2QuB/1/

您可以考慮使用jquery deferreds(或deferreds的其他實現),它可以非常優雅地處理此模式。

需要注意的重要一點是,延遲完成的回調將按添加的順序執行。

 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(); 

演示

HTML5規范草案指出setTimeout方法可以異步運行(這意味着可能不會保留執行回調的順序),這可能是瀏覽器正在執行的操作。

setTimeout()方法必須運行以下步驟:

...

6.返回句柄,然后繼續異步運行此算法。

7.如果方法上下文是Window對象,請等待直到與該方法上下文關聯的Document完全處於活動狀態達另一個超時毫秒(不必連續)。

在任何情況下,都可以通過執行以下操作來解決此問題:

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
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM