I am trying to run multiple asynchronous tasks in series using promises. Each task should run right after the previous one finishes. This is simplified example what I have tried:
var order = [];
var tasks = [
new Promise(resolve => {
order.push(1);
setTimeout(() => {
order.push(2)
resolve();
}, 100);
}),
new Promise(resolve => {
order.push(3);
setTimeout(() => {
order.push(4)
resolve();
}, 100);
}),
new Promise(resolve => {
order.push(5);
resolve();
})
];
tasks.reduce((cur, next) => cur.then(next), Promise.resolve()).then(() => {
console.log(order); // [ 1, 3, 5 ]
});
setTimeout(() => console.log(order), 200); // [ 1, 3, 5, 2, 4 ]
I would expect order
to be equal [ 1, 2, 3, 4, 5 ]
in the callback function. However I got those strange results ( [ 1, 3, 5 ]
in then
callback and [ 1, 3, 5, 2, 4 ]
in delayed function). What am I missing?
When you write something like
new Promise(resolve => {
order.push(1);
setTimeout(() => {
order.push(2)
resolve();
}, 100);
});
it's executed right away, meaning it runs now , and resolves in 0.1 seconds.
It doesn't matter that you write it inside an array, the functions are still ran right now , and the promises are returned as the values in the array.
In other words, all three promise calls run in parallell, they all run right away, with just milliseconds apart, and resolve at the given timen in the internal timer, from now !
If you want to run one promise after the other, they have to somehow be wrapped so they don't run now , but whenever they are called, for instance something like
var tasks = [
_ => new Promise(resolve => {
order.push(1);
setTimeout(() => {
order.push(2)
resolve();
}, 100);
}),
_ => new Promise(resolve => {
order.push(3);
setTimeout(() => {
order.push(4)
resolve();
}, 100);
}),
_ => new Promise(resolve => {
order.push(5);
resolve();
}),
];
(the underscore is valid ES2015 shorthand for an anonymous arrow function)
where each array value is an anonymous function that can be called, and when called the promise constructor runs and returns the promise.
To recursively call the functions in serial, a recursive function call is the easiest, where the next function is called when the current is finished etc.
(function iterate(i) {
tasks[i]().then(() => { // when done
if (tasks[++i]) iterate(i); // call the next one
});
})(0);
Edit:
You could also Array.reduce
the way you're already doing it, now that you have functions that returns promises
tasks.reduce((cur, next) => cur.then(next), Promise.resolve()).then(() => {
// all done, chain is complete !
});
You're missing the fact that when you're using setTimeout
, the callbacks (that push 2
, 4
and log order
) will be executed on the next iteration of the event loop , ie the next 'tick'. While all other functions (the Promise
constructors and the reduce
callback) are being executed immediately , ie in the current 'tick'.
var order = [];
var tasks = [
new Promise(resolve => {
order.push(1); // 1. callback executes immediately pushing 1 into order
setTimeout(() => { // 2. setTimeout executes, pushing the callback into the event queue after 100ms
order.push(2) // 8. callback executes, pushing 2 into order
resolve();
}, 100);
}),
new Promise(resolve => {
order.push(3); // 3. callback executes immediately pushing 3 into order
setTimeout(() => { // 4. setTimeout executes, pushing the callback into the event queue after 100ms
order.push(4) // 9. callback executes, pushing 4 into order
resolve();
}, 100);
}),
new Promise(resolve => {
order.push(5); // 5. callback executes immediately pushing 5 into order
resolve();
})
];
console.log(order); // [ 1, 3, 5 ]
// 6. reduce executes immediately, executes Promise.resolve which logs order and then loops through order and executes the callback everytime
tasks.reduce((cur, next) => cur.then(next), Promise.resolve()).then(() => {
console.log(order); // [ 1, 3, 5 ]
});
setTimeout(() => {
console.log(order); // 10. callback executes and logs order
}, 200); // 7. setTimeout executes, pushing the callback into the event queue after 200ms
Only After steps 1 through 7 happen, all callbacks that where pushed into the event queue (by setTimeout
) will be executed, ie pushed on the call stack, clearing the event queue and eventually clearing the call stack, after said callbacks execute (steps 8, 9 and 10).
Note that functions are only popped off the event queue when the call stack is empty.
This is a promise-less and slightly strange way of doing things, but it seems to work:
"use strict";
var order = [];
var i = 0;
function next(){
if(tasks[++i]) tasks[i]()
}
var tasks = [
function() {
order.push(1);
setTimeout(() => {
order.push(2)
next()
}, 100);
},
function() {
order.push(3);
setTimeout(() => {
order.push(4)
next();
}, 100);
},
function() {
order.push(5);
next()
console.log(order)
}
];
tasks[0]()
每个异步函数都有一个同步部分,该设置通向promise的(同步)返回。
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.