我使用Promises相当新,并且很难绕过jQuery deferreds。
我目前拥有的是一系列函数,我在某一点执行:
while (queue.length) {
(queue.shift())();
}
这个问题是,其中一些函数是异步的,但是我需要它们一个接一个地运行。
因此队列中的一些函数返回延迟(例如通过jQuery.ajax()),有些只是普通函数。我想知道如何以顺序+顺序执行它们(仅在前一个函数完成时执行下一个函数)。
我想也许jQuery.when会是我想要的,但我无法弄清楚它是怎么回事,我只是想到这个:
var deferred = jQuery.Deferred();
while (queue.length > 0) {
deferred.done().then(queue.shift());
}
答案 0 :(得分:28)
非常精明,jQuery.when
是您正在寻找的东西。它需要一个promise或一个普通的non-thenable并返回一个超过该值的promise。但是,由于这里有函数而不是值,因此这不是.then
的行为。
var queue = [syncFunction, syncFunc2, returnsPromiseAsync];
var d = $.Deferred().resolve();
while (queue.length > 0) {
d = d.then(queue.shift()); // you don't need the `.done`
}
(fiddle)
或者,您可以减少;
var res = queue.reduce(function(prev,cur){ // chain to res later to hook on done
return prev.then(cur);
}, $.Deferred().resolve());
如果你有更多的代码在这之下做事,因为队列中的函数可以同步或异步运行(或者某些同步,然后我们遇到异步),为了避免混乱,你可能想要确保函数始终运行异步。您可以通过将resolve
打包在Deferred
的初始setTimeout
上来轻松完成此操作:
var p = $.Deferred();
var res = queue.reduce(function(prev,cur){ // chain to res later to hook on done
return prev.then(cur);
}, p);
setTimeout(p.resolve.bind(p), 0);
现在,在超时发生之前,进程不会启动,并且此后的任何代码肯定会在队列中的第一个函数执行之前运行。在我看来,这与jQuery的承诺的语义不一致(因为they are inconsistent about sync vs. async callbacks),但你可能想要一致性。
如果您需要知道流程何时完成,只需在.then
上使用res
处理程序:
res.then(function() {
// All functions have now completed
});
对于这些情况,这里有一个简单的包装函数,可以同时执行这两个操作:
function clearQueue(q) {
var p = $.Deferred();
setTimeout(p.resolve.bind(p), 0);
return q.reduce(function(prev,cur){
return prev.then(cur);
}, p);
}
使用示例(fiddle):
clearQueue(queue).then(function() {
console.log("All done");
});
console.log("This runs before any queued function starts");