javascript jQuery Deferreds 的异步循环(承诺)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15504921/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Asynchronous Loop of jQuery Deferreds (promises)
提问by Jon Wells
I am trying to create what I think is referred to as a "Waterfall". I want to sequentially process an array of async functions (jQuery promises).
我正在尝试创建我认为被称为“瀑布”的东西。我想按顺序处理一组异步函数(jQuery 承诺)。
Here's a contrived example:
这是一个人为的例子:
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");
I would like it to complete the task in the order they are executed (present in the array). So, in this example I want it to do task 1 and wait for it to resolve then do task 2 wait for it to resolve, do task 3 etc and the log "all done".
我希望它按照执行的顺序(存在于数组中)完成任务。因此,在此示例中,我希望它执行任务 1 并等待它解决,然后执行任务 2 等待它解决,执行任务 3 等,并记录“全部完成”。
Maybe this is really obvious but I've been trying to figure this out all afternoon.
也许这真的很明显,但我整个下午都在试图弄清楚这一点。
采纳答案by Rocket Hazmat
I'd try using $().queue
instead of $.Deferred
here. Add the functions to a queue, and only call the next one when ready.
我会尝试使用$().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');
回答by Bergi
For a waterfall, you need an async loop:
对于瀑布,您需要一个异步循环:
(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");
});
回答by thedarklord47
You can create a resolved $.Deferred and just add to the chain with each iteration:
您可以创建一个已解析的 $.Deferred 并在每次迭代时添加到链中:
var dfd = $.Deferred().resolve();
tasks.forEach(function(task){
dfd = dfd.then(function(){
return doTask(task);
});
});
Step by step the following is happening:
逐步发生以下情况:
//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);
});
});
Personally, I find this cleaner than recursion and more familiar than $().queue (jQuery API for $().queue is confusing as it is designed for animations, it is also likely you are using $.Deferred's in other places in your code). It also has the benefits of standard transfer of results down the waterfall through resolve() in the async operation and allowing the attachment of a $.done property.
就我个人而言,我发现这比递归更简洁,也比 $().queue 更熟悉($().queue 的 jQuery API 令人困惑,因为它是为动画设计的,也可能你在你的其他地方使用了 $.Deferred's代码)。它还具有通过异步操作中的 resolve() 沿瀑布标准传输结果的好处,并允许附加 $.done 属性。
Here it is in a jsFiddle
这是在jsFiddle 中
回答by Andrew Kirkegaard
Have a look at the $.whenand thenmethods for running deferreds.
Waterfalls are used to pipe return values from one deferred to the next, in series. It would look something like this.
瀑布用于将返回值从一个递延到下一个的管道串联。它看起来像这样。
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"); });
Note the argument passed to resolve
. That gets passed to the next function in the chain. If you just want to run them in series without piping in arguments, you can take that out and change the reduce call to .reduce(function(chain, taskNum) { return chain.then(doTask.bind(null, taskNum)); }, doTask(tasks[0]));
请注意传递给 的参数resolve
。这将传递给链中的下一个函数。如果您只想在没有管道参数的情况下连续运行它们,您可以将其取出并将reduce调用更改为.reduce(function(chain, taskNum) { return chain.then(doTask.bind(null, taskNum)); }, doTask(tasks[0]));
And in parallel it would look like this:
同时它看起来像这样:
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.
});
回答by awbergs
Interesting challenge indeed. What I have come up with is a recursive function that accepts a list and an optional start index.
确实很有趣的挑战。我想出的是一个递归函数,它接受一个列表和一个可选的起始索引。
Here is a link to the jsFiddlethat I have tested with a few different list lengths and intervals.
这是我用几个不同的列表长度和间隔测试过的 jsFiddle 的链接。
I'm assuming you have a list of functions that return promises (not a list of numbers). If you do have a list of numbers you would change this part
我假设您有一个返回承诺的函数列表(不是数字列表)。如果你有一个数字列表,你会改变这部分
$.when(tasks[index]()).then(function(){
deferredSequentialDo(tasks, index + 1);
});
to this
对此
/* 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);
});
I'm not sure how big your list of functions could be but just be aware that the browser will hold on to the resources from the first deferredSequentialDo call until they are all finished.
我不确定您的函数列表有多大,但请注意浏览器将从第一个 deferredSequentialDo 调用开始保留资源,直到它们全部完成。
回答by Mathias Markl
Arguments
参数
- items: array of arguments
- func: the async function
- callback: callback function
- update: update function
- 项目:参数数组
- func:异步函数
- 回调:回调函数
- 更新:更新功能
Simple Loop:
简单循环:
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);
Track Progress:
跟踪进度:
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);