web-dev-qa-db-ja.com

jQuery Deferreds(promises)の非同期ループ

私は「滝」と呼ばれるものを作ろうとしています。非同期関数の配列を順番に処理したい(jQuerypromise)。

不自然な例を次に示します。

function doTask(taskNum){
    var dfd = $.Deferred(), 
        time = Math.floor(Math.random()*3000);

    setTimeout(function(){
        console.log(taskNum);
        dfd.resolve();
    },time)

    return dfd.promise();
}

var tasks = [1,2,3];

for (var i = 0; i < tasks.length; i++){
    doTask(tasks[i]);
}

console.log("all done");

実行された順序(配列に存在する)でタスクを完了させたいのですが。したがって、この例では、タスク1を実行して解決するのを待ってから、タスク2を実行して解決するのを待ち、タスク3などを実行してログを「すべて完了」にします。

たぶんこれは本当に明白ですが、私は午後中ずっとこれを理解しようとしています。

22
Jon Wells

ここでは、_$.Deferred_の代わりに$().queueを使用してみます。関数をキューに追加し、準備ができたときにのみ次の関数を呼び出します。

_function doTask(taskNum, next){
    var time = Math.floor(Math.random()*3000);

    setTimeout(function(){
        console.log(taskNum);
        next();
    },time)
}

function createTask(taskNum){
    return function(next){
        doTask(taskNum, next);
    }
}

var tasks = [1,2,3];

for (var i = 0; i < tasks.length; i++){
    $(document).queue('tasks', createTask(tasks[i]));
}

$(document).queue('tasks', function(){
    console.log("all done");
});

$(document).dequeue('tasks');
_
21
Rocket Hazmat

ウォーターフォールの場合、非同期ループが必要です。

(function step(i, callback) {
    if (i < tasks.length)
        doTask(tasks[i]).then(function(res) {
            // since sequential, you'd usually use "res" here somehow
            step(i+1, callback);
        });
    else
        callback();
})(0, function(){
    console.log("all done");
});
9
Bergi

解決された$ .Deferredを作成し、反復ごとにチェーンに追加することができます。

var dfd = $.Deferred().resolve();
tasks.forEach(function(task){
    dfd = dfd.then(function(){
        return doTask(task);
    });
});

ステップバイステップで次のことが起こっています:

//begin the chain by resolving a new $.Deferred
var dfd = $.Deferred().resolve();

// use a forEach to create a closure freezing task
tasks.forEach(function(task){

    // add to the $.Deferred chain with $.then() and re-assign
    dfd = dfd.then(function(){

        // perform async operation and return its promise
        return doTask(task);
    });

});

個人的には、これは再帰よりもクリーンで、$()。queueよりも使い慣れていると思います($()。queueのjQuery APIはアニメーション用に設計されているため混乱します。また、他の場所で$ .Deferredを使用している可能性もありますコード)。また、非同期操作のresolve()を介してウォーターフォールに結果を標準で転送し、$。doneプロパティをアタッチできるという利点もあります。

ここにあります jsFiddle

7
thedarklord47

延期を実行するための $。when および then メソッドを見てください。

ウォーターフォールは、戻り値を1つの遅延から次の遅延に直列にパイプするために使用されます。それは何かに見えるでしょう このように

_function doTask (taskNum) {
  var dfd = $.Deferred(),
      time = Math.floor(Math.random() * 3000);

  console.log("running task " + taskNum);

  setTimeout(function(){
      console.log(taskNum + " completed");
      dfd.resolve(taskNum + 1);
  }, time)

  return dfd.promise();
}

var tasks = [1, 2, 3];

tasks
  .slice(1)
  .reduce(function(chain) { return chain.then(doTask); }, doTask(tasks[0]))
  .then(function() { console.log("all done"); });
_

resolveに渡された引数に注意してください。それはチェーン内の次の関数に渡されます。引数をパイプせずにそれらを直列に実行したいだけの場合は、それを取り出して、reduce呼び出しを.reduce(function(chain, taskNum) { return chain.then(doTask.bind(null, taskNum)); }, doTask(tasks[0]));に変更できます。

そして並行してそれは見えるでしょう このように

_var tasks = [1,2,3].map(function(task) { return doTask(task); });

$.when.apply(null, tasks).then(function() { 
    console.log(arguments); // Will equal the values passed to resolve, in order of execution.
});
_
5

引数

  • アイテム:引数の配列
  • func:非同期関数
  • コールバック:コールバック関数
  • 更新:更新機能

単純なループ:

var syncLoop = function(items, func, callback) {
    items.reduce(function(promise, item) {
        return promise.then(func.bind(this, item));
    }, $.Deferred().resolve()).then(callback);
};

syncLoop(items, func, callback);

進捗状況の追跡:

var syncProgress = function(items, func, callback, update) {
    var progress = 0;
    items.reduce(function(promise, item) {
        return promise.done(function() {
            update(++progress / items.length);
            return func(item);
        });
    }, $.Deferred().resolve()).then(callback);
};

syncProgress(items, func, callback, update);
0
Mathias Markl

確かに興味深い挑戦。私が思いついたのは、リストとオプションの開始インデックスを受け入れる再帰関数です。

ここにjsFiddleへのリンクがあります いくつかの異なるリストの長さと間隔でテストしました。

約束を返す関数のリスト(数値のリストではない)があると仮定します。番号のリストがある場合は、この部分を変更します

$.when(tasks[index]()).then(function(){
    deferredSequentialDo(tasks, index + 1);
});

これに

/* Proxy is a method that accepts the value from the list
   and returns a function that utilizes said value
   and returns a promise  */
var deferredFunction = myFunctionProxy(tasks[index]);

$.when(tasks[index]()).then(function(){
    deferredSequentialDo(tasks, index + 1);
});

関数のリストがどれほど大きいかはわかりませんが、ブラウザーが最初のdeferredSequentialDo呼び出しからすべてが完了するまで、リソースを保持することに注意してください。

0
awbergs