1

奇妙な動作に気付きました。一連のタスクがあり、それらの実行を延期したい場合、それぞれの遅延が 0 の setTimeout を使用できます。( http://javascript.info/tutorial/events-and-timing-depth#the-settimeout-func-0-trickを参照)

すべてが完璧に機能します。タスクはキューに入れられ、できるだけ早く実行されます。

しかし...さまざまなsetTimeoutの呼び出しが非常に近い場合、(めったに起こりません!)正しい順序で実行されないことがあります。なんで?

4

3 に答える 3

3

このパターンを非常にエレガントに処理できるjquerydeferreds(または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(); 

デモ

于 2013-02-15T08:28:39.267 に答える
3

「正しい」順序で起動されるとは誰も約束していません (同じタイムアウトのタスクは、タイムアウトに設定された順序で実行されます)。setTimeout次のことのみを保証します。

  • 各タイムアウトは正確に 1 回実行されます (その間にページが停止しない限り)
  • 各タイムアウトは、想定されているよりも早く実行されます。

実行順序についての言葉はありません。実際、実装者が (副作用としても) 順序を維持しようとしたとしても、すべてのタスクとバイナリ ヒープ (ここでよく使用される可能性がある) に一意の並べ替え順序を提供するのに十分な時間分解能がない可能性が最も高いです。等しいキーの挿入順序は保持されません)。

延期されたタスクの順序を維持したい場合は、前のタスクが完了したときに 1 つだけキューに入れる必要があります。

これはうまくいくはずです:

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/

于 2013-02-15T08:02:41.983 に答える
0

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
}
于 2013-02-15T08:03:39.973 に答える