After having read the absolutely wonderful exploring ES6, I wanted to use my newly acquired ES6 skills in a new project. And promises were always the crown jewel of esoteric topics to me (after monads of course :P).
Finally a new project came along, and I excitedly sat down to apply all my knowledge into practice. I started nice and easy, moved on to Promise.all()
to load multiple promises in parallel, but then a use case cropped up, where I had to load promises in series. No sweat, just head over to SO, and look up the answer. Surely, I am not the only one here with this requirement. Sadly, most of the answers pointed to using async and other similar libraries. Nevertheless, I did get an answer which just used plain ES6 code to do that. Aww yiss ! Problemo solved.
I couldn’t declare the functions in an array like the example. Because I had a single function. I modified the code a bit to adjust for my usecase. This was how it came out -
'use strict';
const load = require('request');
let myAsyncFuncs = [
computeFn(1),
computeFn(2),
computeFn(3)
];
function computeFn(val) {
return new Promise((resolve, reject) => {
console.log(val);
// I have used load() but this can be any async call
load('http://exploringjs.com/es6/ch_promises.html', (err, resp, body) => {
if (err) {
return reject(err);
}
console.log("resolved")
resolve(val);
});
});
}
myAsyncFuncs.reduce((prev, curr) => {
console.log("returned one promise");
return prev.then(curr);
}, Promise.resolve(0))
.then((result) => {
console.log("At the end of everything");
})
.catch(err => {
console.error(err);
});
Not so fast. As you can guess, it didn’t work out. This was the output I got -
1
2
3
returned one promise
returned one promise
returned one promise
At the end of everything
resolved
resolved
resolved
The promises were all getting pre-executed and didn’t wait for the previous promise to finish. What is going on ? After some more time, got this (Advanced mistake #3: promises vs promise factories).
Aha ! So the promise will start to execute immediately on instantiation. And will resolve only when called. So all I had to do was delay the execution of the promise until the previous promise was finished. bind
to the rescue !
'use strict';
const load = require('request');
let myAsyncFuncs = [
computeFn.bind(null, 1),
computeFn.bind(null, 2),
computeFn.bind(null, 3)
];
function computeFn(val) {
return new Promise((resolve, reject) => {
console.log(val);
// I have used load() but this can be any async call
load('http://exploringjs.com/es6/ch_promises.html', (err, resp, body) => {
if (err) {
return reject(err);
}
console.log("resolved")
resolve(val);
});
});
}
myAsyncFuncs.reduce((prev, curr) => {
console.log("returned one promise");
return prev.then(curr);
}, Promise.resolve(0))
.then((result) => {
console.log("At the end of everything");
})
.catch(err => {
console.error(err);
});
And now -
returned one promise
returned one promise
returned one promise
1
resolved
2
resolved
3
resolved
At the end of everything
Finally :)
Conclusion - If you want to execute promises in series, dont create promises which start executing. Delay their execution untill the previous promise has finished.