Imagine you have an asynchronous process, which is a cascade of n async steps:
function step1(item){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('step 1 fort item ' + item);
});
});
}
function step2(item){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('step 2 for item ' + item);
},1000);
});
}
function step3(item){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('step 3 for item ' + item);
},1000);
});
}
function processItem(item){
let steps = [step1, step2, step3];
return steps.reduce((current, next) => {
return current.then(res => {
console.log(res);
return next(item);
}).then(res => {
console.log(res);
});
},Promise.resolve());
}
So now you have an array of items, and you want to process all the elements using the processItem function for each of them. But for limitations, all processes must be performed sequentially, one begins when the previous one is completed.
well, if I implement it this way:
let items = [1, 2, 3];
items.map(i => processItem(i)).reduce((p, next) => {
return p.then(() => {
return next;
});
}).then(() => {
// all finished
});
You get this output:
step 1 for item 1
step 1 for item 2
step 1 for item 3
step 2 for item 1
step 2 for item 2
step 2 for item 3
step 3 for item 1
step 3 for item 2
step 3 for item 3
and takes only 3 seconds, not 9 as expected.
Meanwhile, if you implement this, avoiding the step of the map, you will get the expected result:
let items = [1, 2, 3];
items.reduce((promise, nextItem) => {
return promise.then(() => {
return processItem(nextItem);
});
}, Promise.resolve()).then(() => {
// all finished
});
Result:
step 1 for item 1
step 2 for item 1
step 3 for item 1
step 1 for item 2
step 2 for item 2
step 3 for item 2
step 1 for item 3
step 2 for item 3
step 3 for item 3
And waiting for 9 seconds.
? , , , promises, promises, , . .