jQuery Deferreds的异步循环(承诺)

Jon*_*lls 21 javascript jquery waterfall jquery-deferred

我想创造我认为被称为"瀑布"的东西.我想顺序处理一系列异步函数(jQuery promises).

这是一个人为的例子:

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");
Run Code Online (Sandbox Code Playgroud)

我希望它按照它们执行的顺序完成任务(存在于数组中).因此,在这个例子中,我希望它执行任务1并等待它解决然后执行任务2等待它解决,执行任务3等并且日志"全部完成".

也许这是非常明显的,但我一直试图在整个下午解决这个问题.

Roc*_*mat 21

我试着用$().queue而不是在$.Deferred这里.将函数添加到队列中,并在准备好时仅调用下一个函数.

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');
Run Code Online (Sandbox Code Playgroud)


Ber*_*rgi 9

对于瀑布,您需要一个异步循环:

(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");
});
Run Code Online (Sandbox Code Playgroud)


the*_*d47 6

您可以创建已解析的$ .Deferred,并在每次迭代时添加到链中:

var dfd = $.Deferred().resolve();
tasks.forEach(function(task){
    dfd = dfd.then(function(){
        return doTask(task);
    });
});
Run Code Online (Sandbox Code Playgroud)

一步一步发生以下情况:

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

});
Run Code Online (Sandbox Code Playgroud)

就个人而言,我发现这比递归更清晰,比$().队列(jQuery API for $().队列更令人困惑,因为它是为动画而设计的,你也可能在你的其他地方使用$ .Deferred's码).它还具有以下优点:通过异步操作中的resolve()并允许附加$ .done属性,在瀑布下标准地传输结果.

这是一个jsFiddle


And*_*ard 5

看看$ .when之后的运行延期的方法。

瀑布用于将返回值从一个递延序列传递到另一个递延序列。看起来像这样

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"); });
Run Code Online (Sandbox Code Playgroud)

注意传递给的参数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.
});
Run Code Online (Sandbox Code Playgroud)