Reputation: 12552
I can't seem to find the right solution to this problem. I have a promise with a bunch of thenables and in one of the resolvers, I want to pause and do a series of work on an array (using promises), and I don't want this to actually resolve until the series of promises have completed. This example seems to get me pretty close, but I think I might have created an anti-pattern.
http://taoofcode.net/promise-anti-patterns/
// async_series.js
// example of using reduce and promises and closure to create async results in series
var q = require('Q');
var results = [1, 2, 3, 4, 5];
function workCollection(arr) {
return arr.reduce(function(promise, item, index) {
return promise.then(function(result) {
return (function(i, r, idx){
setTimeout(function(){
console.log('item', i, 'result', r, 'index', idx);
}, 1000 * idx);
return true
})(item, result, index);
});
}, q().then(function(){return true}));
}
q()
.then(function(){
console.log('start');
})
.then(function(){
workCollection(results)
})
.then(function(){
console.log('done');
})
.catch(function(err){
console.log(err);
});
Kind of works, but the output is:
start
done
item 1 result true index 0
item 2 result true index 1
item 3 result true index 2
item 4 result true index 3
item 5 result true index 4
as opposed to this (intended):
start
item 1 result true index 0
item 2 result true index 1
item 3 result true index 2
item 4 result true index 3
item 5 result true index 4
done
Suggestions greatly appreciated. Plz comment before down voting.
Upvotes: 0
Views: 427
Reputation: 664559
There's multiple things:
return
the (promise) result of `` from the then
callback, otherwise the chain won't wait for itq().then(function(){return true})
should simply be q(true)
reduce
callback already provides it.return true
and expect anything to wait for the setTimeout
. You'll need to promisify it (wrap it in a new Q.Promise(…)
), or just use Q.delay
.All in all, your code should look like this:
var q = require('Q');
function workCollection(arr) {
return arr.reduce(function(promise, item, index) {
return promise.then(function(result) {
return Q.delay(result, 1000);
}).then(function(result) {
console.log('item', item, 'result', result, 'index', index);
return true;
});
}, q(true));
}
q().then(function(){
console.log('start');
return [1, 2, 3, 4, 5];
}).then(workCollection).then(function(result) {
console.log('done', result);
}, function(err){
console.error(err);
});
Upvotes: 1
Reputation: 25034
the mistake you are making is( other than not returning workCollection
promise) setTimeout
is not promisified, you can fix it like:
var q = require('Q');
var results = [1, 2, 3, 4, 5];
function delayedCall(i, r, idx){
return q.Promise(function(resolve, reject){
setTimeout(function(){
console.log('item', i, 'result', r, 'index', idx);
resolve(true);
}, 1000 * idx);
});
}
function workCollection(arr) {
return arr.reduce(function(promise, item, index) {
return promise.then(function(result) {
return delayedCall(item, result, index);
});
}, q(true));
}
q().then(function(){
console.log('start');
}).then(function(){
return workCollection(results)
}).then(function(){
console.log('done');
}).catch(function(err){
console.log(err);
});
fiddle demo, I have used Q.defer
instead of Q.Promise
but rest of code is same
Upvotes: 1
Reputation: 11509
You can achieve this with promise.all()
. Just push each of your array promises into an array (often name deferred
) and then return promise.all(deferred)
Documentation here: https://github.com/kriskowal/q/wiki/API-Reference#promise-for-array-methods
Update
Rather than edit your fiddle, I created one that uses deferred
, which is what you should be using in this case. https://jsfiddle.net/twler/aujqcmhv/1/
Hopefully it will clear up a bit about promise objects, as they can be confusing.
Upvotes: 1
Reputation: 731
I think you are getting done
right after start
as you are not returning result of workCollection(results)
in your then so its getting resolved with undefined
right away. Try this
.then(function(){
return workCollection(results)
})
Also, as suggested, go with Promise.all
if the order doesn't matter otherwise your Promise Waterfall
approach as also described here is fine.
Upvotes: 1