How Can I Chain Together Groups Of Promises?
Solution 1:
This can be done by first pre-processing the array of items into groups, then applying the two patterns (not the anti-patterns) provided here under the heading "The Collection Kerfuffle".
The main routine can be coded as a single chain of array methods.
var work_items = [ 'A','B','C','D','E','F','G','H','I' ];
var wait = 3000;
//Async worker functionfunctiongetWorkPromise(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;
};
functiondoAsyncStuffInGroups(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) {
returngetWorkPromise(item);
}));
});
}, Q());
}
doAsyncStuffInGroups(work_items, 2).then(function() {
console.log("All done");
});
See fiddle. Delay of 3s gives you time to appreciate what's going on. I found 1s too quick.
Solutions like this are elegant and concise but pretty well unreadable. In production code I would provide more comments to help whoever came after me.
For the record:
- The opening
arr.map(...).filter(...)
processesarr
(non destructively) into an array of arrays, each inner array representing a group of length n (plus terminal remainders). - The chained
.reduce(...)
is an async "serializer" pattern. - The nested
Q.all(group.map(...))
is an async "parallelizer" pattern.
Solution 2:
The .then
function of a promise does not mutate the promise, so when you do:
p.then(function(){
// stuff
});
You do not change the promise p
at all, instead, you need to assign it to something:
p = p.then(....)
This is why your queue
promise was always resolved, it never changed beyond Q()
.
In your case, something like changing:
queue.then(Q.all(unit));
Into:
queue = queue.then(function(){ return Q.all(unit); });
Or in ES6 promises and libraries that use their syntax like Bluebird the other answer mentioned:
queue = queue.then(function(){ returnPromise.all(unit); });
Solution 3:
The thing that confused me most is that the async function being chained needs to return a function that returns a promise. Here's an example:
functionsetTimeoutPromise(ms) {
returnnewPromise(function (resolve) {
setTimeout(resolve, ms);
});
}
functionfoo(item, ms) {
returnfunction() {
return setTimeoutPromise(ms).then(function () {
console.log(item);
});
};
}
var items = ['one', 'two', 'three'];
functionbar() {
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');
});
Notice that foo returns a function that returns a promise. foo() does not return a promise directly.
See this Live Demo
Solution 4:
i would suggest you use bluebird, its the best performance promise out there, https://github.com/petkaantonov/bluebird
the example to chain should also be here https://github.com/petkaantonov/bluebird#how-do-long-stack-traces-differ-from-eg-q
Post a Comment for "How Can I Chain Together Groups Of Promises?"