我正在尝试使用promises串行运行多个异步任务。每个任务应在上一个任务完成后立即运行。这是我尝试过的简化示例:
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 ]
我希望order
在回调函数中等于[ 1, 2, 3, 4, 5 ]
。但是我得到了那些奇怪的结果([ 1, 3, 5 ]
回调中的then
和延迟函数中的[ 1, 3, 5, 2, 4 ]
。我错过了什么?
答案 0 :(得分:5)
当你写
之类的东西时new Promise(resolve => {
order.push(1);
setTimeout(() => {
order.push(2)
resolve();
}, 100);
});
它会立即执行,意味着它会现在,并在0.1秒后结算。
你在数组中写它并不重要,函数仍然是现在,并且promises作为数组中的值返回。
换句话说,所有三个承诺调用都是并行运行的,它们都会立即运行,只需几毫秒,并在内部计时器的给定时间内解析,从现在!
如果你想在另一个之后运行一个承诺,它们必须以某种方式被包装,以便它们不会运行现在,但是无论何时它们被调用,例如
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();
}),
];
(下划线是有效的ES2015匿名箭头功能简写)
其中每个数组值都是可以调用的匿名函数,并且在调用时,promise构造函数会运行并返回promise。
要以串行方式递归调用函数,递归函数调用是最简单的,当电流结束时调用下一个函数等。
(function iterate(i) {
tasks[i]().then(() => { // when done
if (tasks[++i]) iterate(i); // call the next one
});
})(0);
编辑:
你也可以Array.reduce
你现在已经这样做了,现在你有了返回承诺的函数
tasks.reduce((cur, next) => cur.then(next), Promise.resolve()).then(() => {
// all done, chain is complete !
});
答案 1 :(得分:0)
您错过了当您使用setTimeout
时,回调(推送2
,4
和日志order
)将在< strong>事件循环的下一次迭代,即下一个'tick'。所有其他功能(Promise
构造函数和reduce
回调)正在立即执行,即在当前的“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
仅在步骤1到7之后,所有被推入事件队列的回调(由setTimeout
)将被执行,即推送到调用堆栈,清除事件队列在所述回调执行后(步骤8,9和10),最终清除调用堆栈。
请注意,当调用堆栈为空时,只会从事件队列中弹出函数。
答案 2 :(得分:0)
这是一种没有承诺且略显奇怪的做事方式,但似乎有效:
"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]()
答案 3 :(得分:0)
每个异步函数都有一个同步部分,该设置通向promise的(同步)返回。