[英]How can I chain together groups of promises?
我正在使用Q javascript Promise库,并且正在浏览器中运行,并且我想弄清楚如何将Promise组链接在一起,以便每个组依次执行。 例如,如果我有项目A,B,C和D,我想将A和B分组在一起,然后将C和D分组在一起,以便A和B在执行C和D之前都必须满足。 我创建了这个简单的jsfiddle以显示我当前的尝试。
var work_items = [ 'A','B','C','D','E','F','G','H','I' ];
var n = 2; // group size
var wait = 1000;
var getWorkPromiseFn = function (item) {
log("Getting promise function for " + item);
return function () {
log("Starting " + item);
var deferred = Q.defer();
setTimeout(function () {
var status = "Finished " + item;
log(status);
deferred.resolve(status);
}, wait);
return deferred.promise;
};
};
var queue = Q();
//log('Getting sequentially'); // One-by-one in sequence works fine
//work_items.forEach(function (item) {
// queue = queue.then(getWorkPromiseFn(item));
//});
log('Getting ' + n + ' at a time'); // This section does not
while (work_items.length > 0) {
var unit = [];
for (var i=0; i<n; i++) {
var item = work_items.shift();
if (item) {
unit.push(getWorkPromiseFn(item));
}
}
queue.then(Q.all(unit));
}
var inspect = queue.inspect(); // already fulfilled, though no work is done
看来我可能在这里将错误的数组传递给Q.all
,因为我传递的是一个数组,这些函数返回的是promise,而不是promise的数组。 当我尝试直接在其中使用promise(例如,使用unit.push(Q().then(getWorkPromiseFn(item));
))时,每个承诺的工作都立即开始,并且没有顺序处理。我想我基本上是不清楚以一种适当的方式来延迟组的执行来代表组。
那么,我该如何推迟执行这样的一组诺言呢?
这可以通过以下方式完成:首先将项目数组进行预处理,然后应用标题为“ The Collection Kerfuffle”下此处提供的两种模式(非反模式)。
主例程可以编码为数组方法的单链。
var work_items = [ 'A','B','C','D','E','F','G','H','I' ];
var wait = 3000;
//Async worker function
function getWorkPromise(item) {
console.log("Starting " + item);
var deferred = Q.defer();
setTimeout(function () {
var status = "Finished " + item;
console.log(status);
deferred.resolve(status);
}, wait);
return deferred.promise;
};
function doAsyncStuffInGroups(arr, n) {
/*
* Process original array into groups, then
* process the groups in series,
* progressing to the next group
* only after performing something asynchronous
* on all group members in parallel.
*/
return arr.map(function(currentValue, i) {
return (i % n === 0) ? arr.slice(i, i+n) : null;
}).filter(function(item) {
return item;
}).reduce(function(promise, group) {
return promise.then(function() {
return Q.all(group.map(function(item) {
return getWorkPromise(item);
}));
});
}, Q());
}
doAsyncStuffInGroups(work_items, 2).then(function() {
console.log("All done");
});
见小提琴 。 延迟3秒让您有时间去了解发生了什么。 我发现1太快了。
像这样的解决方案优雅简洁,但是非常难以理解。 在生产代码中,我将提供更多注释,以帮助任何关注我的人。
作为记录:
arr.map(...).filter(...)
将arr
(非破坏性地)处理为一个数组数组,每个内部数组代表一组长度为n的组(加上终端余数)。 .reduce(...)
是异步的“序列化器”模式。 Q.all(group.map(...))
是异步的“并行器”模式。 promise的.then
函数不会使promise发生变化,所以当您这样做时:
p.then(function(){
// stuff
});
您根本不需要更改承诺p
,而是需要将其分配给某些东西:
p = p.then(....)
这就是为什么您的queue
承诺始终得到解决的原因,它从未改变过Q()
。
就您而言,类似更改:
queue.then(Q.all(unit));
进入:
queue = queue.then(function(){ return Q.all(unit); });
或者在ES6的Promise和使用Bluebird之类语法的库中,提到了另一个答案:
queue = queue.then(function(){ return Promise.all(unit); });
最让我困惑的是,被链接的异步函数需要返回一个返回承诺的函数。 这是一个例子:
function setTimeoutPromise(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
function foo(item, ms) {
return function() {
return setTimeoutPromise(ms).then(function () {
console.log(item);
});
};
}
var items = ['one', 'two', 'three'];
function bar() {
var chain = Promise.resolve();
for (var i in items) {
chain = chain.then(foo(items[i], (items.length - i)*1000));
}
return chain.then();
}
bar().then(function () {
console.log('done');
});
注意,foo返回一个返回promise的函数 。 FOO() 不直接返回一个承诺。
观看此现场演示
我建议您使用bluebird,它是目前性能最好的承诺, https://github.com/petkaantonov/bluebird
链接示例也应该在这里https://github.com/petkaantonov/bluebird#how-do-long-stack-traces-differ-from-eg-q
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.