我正在研究 JS 中的异步行为,并且大部分情况下进展顺利。我了解执行代码的同步方式,JS的单线程以及诸如setTimeout内部的回调如何由Web浏览器API定时,然后添加到任务队列中。
事件循环会不断地检查调用栈,只有当它为空时(所有同步代码都已执行),它才会取走已经在任务队列中排队的函数。将它们推回调用堆栈并执行。
这很简单,这就是下面代码的原因:
console.log('start');
setTimeout(() => console.log('timeout'), 0);
console.log('end');
会输出start, end, timeout
。
现在,当我开始阅读有关 Promise 的内容时,我了解到它们比常规异步代码(例如超时、间隔、事件侦听器)具有更高的优先级,而是被放置在作业队列/微任务队列中。事件循环将首先优先考虑该队列并运行所有作业,直到耗尽,然后再进入任务队列。
这仍然是有道理的,可以通过运行看到:
console.log('start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('end');
这输出start, end, promise, timeout
. 同步代码执行,then
回调从微任务队列推送到堆栈并执行,任务队列中的 setTimeout 回调任务被推送并执行。到目前为止一切都很好。
正如官方文档中所说,我可以围绕上面的例子来回想一下,promise 会立即同步地得到解决。如果我们要使用 new 关键字创建一个 Promise 并提供一个 executor 函数,也会发生同样的情况。该执行器函数将同步执行并解析该函数。因此,当遇到 then 时,它可以在已解决的 Promise 上异步运行。
console.log('start');
const p1 = new Promise(resolve => {
console.log('promise 1 log');
resolve('promise 1');
});
p1.then(msg => console.log(msg));
console.log('end');
上面的代码片段将输出start, promise 1 log, end, promise 1
证明执行器同步运行。
这就是我对承诺感到困惑的地方,假设我们有以下代码:
console.log('start');
const p1 = new Promise(resolve => {
console.log('promise 1 log');
setTimeout(() => {
resolve('promise 1');
}, 0);
});
p1.then(msg => console.log(msg));
console.log('end');
这将导致start, promise 1 log, end, promise 1
. 如果 executor 函数立即执行,这意味着其中的 setTimeout 将被放入任务队列以供稍后执行。据我了解,这意味着承诺现在仍在等待中。我们得到then
方法和其中的回调。这将被放入作业队列中。其余的同步代码被执行,我们现在有空的调用堆栈。
据我了解,promise 回调现在将具有优先权,但它如何在仍未解决的 promise 下执行?Promise 应该只在其中的 setTimeout 被执行后才解决,它仍然位于任务队列中。我听说过,没有任何额外的说明,只有在承诺得到解决的情况下才会运行,从我的输出中我可以看到这是真的,但我不明白在这种情况下它是如何工作的。我唯一能想到的是一个异常或类似的东西,以及一个任务队列任务在微任务之前获得优先级。
这最终很长,所以我感谢任何花时间阅读和回答这个问题的人。我很想更好地了解任务队列、作业队列和事件循环,所以不要犹豫发布详细的答案!先感谢您。