我一直在与这个疑问作斗争,因为在事件循环中,作业队列比回调队列具有更高的优先级,即,promise 比 setTimeout 具有更高的优先级,那么呢:
new Promise(resolve => setTimeout(resolve, 0, "Done!")).then((message) => console.log(message));
下面的代码中它的优先级是如何决定的:
function f1() {
console.log('f1');
}
function f2() {
console.log('f2');
}
function f3(){
console.log('f3');
}
function f4(){
console.log('f4');
}
function main() {
console.log('main');
setTimeout(f1, 0);
Promise.resolve().then(() => console.log('r1'));
new Promise(resolve => setTimeout(resolve, 0, "Done!")).then((message) => console.log(message));
f2();
console.log('hello');
setTimeout(f3, 0);
}
setTimeout(f4, 0);
main();
输出为:
main
f2
hello
r1
f4
f1
Done!
f3
main()
中的同步代码首先执行:
console.log('main');
打印main
。setTimeout(f1, 0);
将 f1
安排为宏任务。Promise.resolve().then(() => console.log('r1'));
安排一个微任务来打印 r1
。new Promise(resolve => setTimeout(resolve, 0, "Done!")).then((message) => console.log(message));
安排一个宏任务来解决承诺。f2();
打印f2
。console.log('hello');
打印hello
。setTimeout(f3, 0);
将 f3
安排为宏任务。外部同步代码
main()
:
setTimeout(f4, 0);
将 f4
安排为宏任务。接下来处理微任务(promise):
Promise.resolve().then(() => console.log('r1'));
打印r1
。宏任务按照计划的顺序进行处理:
f4
打印f4
。f1
打印f1
。setTimeout
中的 Promise
解决的承诺会打印 Done!
。f3
打印f3
。这里没有优先顺序。优先级是指允许用户代理 (UA) 选择哪个任务先于另一个任务运行。
对于微任务,它们根本没有选择,当 JS 调用堆栈为空时,它必须执行下一个微任务,无论 JS 是从任务内部执行、在回调执行之后还是在另一个微任务执行之后。
优先级是一件任务的事情,当事件循环从各种任务源中选择一个时(这个算法中的步骤2.1)。例如,UI任务通常比超时任务具有更高的优先级,因此如果同时存在待处理的UI任务和待处理的超时任务,则UA将首先执行UI任务。如果这样运行 JS,每次 JS 调用堆栈为空时都会有一个微任务检查点。
所以当你这样做时
new Promise(resolve => setTimeout(resolve, 0, "Done!"))
.then((message) => console.log(message));
超时任务将执行解析 Promise 的 JS,解析 Promise 会将执行记录消息的 JS 的微任务排队。超时任务中的 JS 执行结束后,由于 JS 调用栈为空,该微任务会“直接”执行。因此,这意味着负责执行记录消息的 JS 的微任务本身就是“计时器任务内部”。只有在该微任务运行完成后,UA 才会继续执行其必须执行的剩余步骤以结束计时器任务。 因此,正如您所看到的,微任务与任务并不对立,它们是不同的东西,尽管两者都允许规范使 UA 执行东西,但整个事件循环的处理模型也是如此。谈论这些不同事情的优先级是没有意义的,因为它们的执行时间是不具有可比性的。