An example from this page. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
var p1 = new Promise(function(resolve, reject){
console.log('p1');
setTimeout(resolve, 5000, "one");
});
var p2 = new Promise(function(resolve, reject){
console.log('p2');
setTimeout(resolve, 3000, "two");
});
var p3 = new Promise(function(resolve, reject){
console.log('p3');
setTimeout(resolve, 2000, "three");
});
var p4 = new Promise(function(resolve, reject){
console.log('p4');
setTimeout(resolve, 1000, "four");
});
var p5 = new Promise(function(resolve, reject){
console.log('p5');
setTimeout(resolve, 4000, "five");
});
Promise.all([p1, p2, p3, p4, p5]).then(function(value) {
console.log(value);
}, function(reason) {
console.log(reason)
});
Output
p1, p2, p3, p4, p5
All p1-p5 are executed at once but let's say if I want to sequence it. Once p1 resolves, then p2 should be called, and then p3.
How can I sequence/chain (instead of parallel) the promises instead of an iteration through all at once.
It's creating callback hell if I do a manual approach on waiting for each promise. Please advise.
Firstly, promises may or may not start executing immediately after creation.
(Note: by immediately, it is relative to the execution loop.)
In your above code, p1
to p5
starts counting down as soon as you leave the current execution loop.
To ensure the promises does not get executed before you want them to, you would have to wrap them in a promise generating function.
var p1 = function(){
return new Promise(function(resolve, reject){
console.log('p1');
setTimeout(resolve, 5000, "one");
});
};
var p2 = function(){
return new Promise(function(resolve, reject){
console.log('p2');
setTimeout(resolve, 3000, "two");
});
};
var p3 = function(){
return new Promise(function(resolve, reject){
console.log('p3');
setTimeout(resolve, 2000, "three");
});
};
var p4 = function(){
return new Promise(function(resolve, reject){
console.log('p4');
setTimeout(resolve, 1000, "four");
});
};
var p5 = function(){
return new Promise(function(resolve, reject){
console.log('p5');
setTimeout(resolve, 4000, "five");
});
};
For the above case, if you already know the number of promises you have, you can simply chain the promises together:
p1().then(p2).then(p3).then(p4).then(p5).then(function(){
// Hurray! All done!
console.log("All done :)");
});
However, if you have a variable amount of promises to chain in sequence, you would have to make use of loops (see @PitaJ's answer) or Array.reduce
.
var arrayOfPromiseGeneratingFunctions = [p1, p2, p3, p4, p5]; // Can be of any size.
// Take the first promise-generating function as chain initializer
var initialFn = arrayOfPromiseGeneratingFunctions.shift();
// Here we're effectively doing promise chaining like the simple solution above
var finalPromise = arrayOfPromiseGeneratingFunctions.reduce(function(previousPromise, fn){
return previousPromise.then(fn)
}, initialFn());
finalPromise.then(function(){
// Last promise called
});
This solution is suitable for an arbitrary number of sequentially executed promises, so as long as you wrap the promises with a function.
Some other catches about this implementation:
1. In the example we wrap p1 to p5 into a function, so that they don't get executed in any way before you want it to.
2. You can pass results promise results from one to another, by adding the parameter in the promise-generating function. Since the first parameter would effectively be the resolve result of the last promise.
Further reading: https://github.com/kriskowal/q#sequences
Promise
is executed immediately on creation. You have to use then
to chain actions.
new Promise(function(resolve, reject){
console.log('p1');
setTimeout(resolve, 5000, "one");
}).then(() => {
console.log('p2');
setTimeout(resolve, 3000, "two");
}).then(() => {
console.log('p3');
setTimeout(resolve, 2000, "three");
}).then(() => {
console.log('p4');
setTimeout(resolve, 1000, "four");
}).then(() => {
console.log('p5');
setTimeout(resolve, 4000, "five");
});
You can do something like this:
function sequential(promiseArr) {
var p = Promise.resolve(), i = 0, l = promiseArr.length;
for (; i < l; i += 1) {
p = p.then(promiseArr[i]);
}
return p;
}
Edit: Fixed it. Would have failed because p
wasn't initialized to start.
Let me rephrase your problem.
You have a function which return a promise. You need to call this function again and again with different arguments only when the previous promise is resolved.
Lets imagine following is the function.
function getPromise(arg) {
return new Promise(function(resolve){
setTimeout(resolve, Math.random() * 10000, [arg]);
})
};
Here is your argument list
var promiseSetup = ['apple', 'orange', 'grapes'];
You can have objects which contain functions to be executed too instead of simple demo params here.
Now I'm going to put down the logic which will execute above promise functions repeatedly with each of the arguments. This will be done in a sequence.
function promiseWaterfall(args, promFunc, idx) {
if (idx >= args.length)
return;
console.log('executing ' + args[idx]);
promFunc(args[idx]).then(function(){
promiseWaterfall(args, promFunc, idx + 1)
});
}
Explanation
The above function is recursive. It will call itself with an advanced index value only when the previous promise it obtained is resolved.
Also note that you can execute a function every time a promise is resolved. You can make the "apple" an apple object like this
[{arg: "apple", func: executeApple}]
Then execute this function where we log the console message.