欢迎来到DIVCSS5查找CSS资料与学习DIV CSS布局技术!
一、实验
 
让我们尝试一个实验。什么执行得更快:立即解决的承诺或立即超时(也就是0毫秒超时)?
 
Promise.resolve(1).then(function resolve() {
 
console.log('Resolved!');
 
});
 
setTimeout(function timeout() {
 
console.log('Timed out!');
 
}, 0);
 
// logs 'Resolved!'
 
// logs 'Timed out!'
 
Promise.resolve(1)是一个静态函数,它返回一个立即解决的承诺。setTimeout(callback, 0)以0毫秒的延迟执行回调。
 
打开演示并检查控制台。您会注意到'Resolved!'首先记录的是 ,然后是'Timeout completed!'。立即解决的承诺比立即超时处理得更快。
 
承诺过程可能会更快,因为Promise.resolve(true).then(...)在setTimeout(..., 0)? 够公平的问题。
 
我们稍微改变一下实验的条件,setTimeout(..., 0)先调用:
 
setTimeout(function timeout() {
 
console.log('Timed out!');
 
}, 0);
 
Promise.resolve(1).then(function resolve() {
 
console.log('Resolved!');
 
});
 
// logs 'Resolved!'
 
// logs 'Timed out!'
 
打开演示并查看控制台。嗯……结果一样!
 
setTimeout(..., 0)之前被调用Promise.resolve(true).then(...)。但是,'Resolved!'仍然记录在'Timed out!'.
 
实验表明,在立即超时之前处理立即解决的承诺。最大的问题是……为什么?
 
2.事件循环
 
与异步 JavaScript 相关的问题可以通过调查事件循环来回答。让我们回顾一下异步 JavaScript 工作原理的主要组成部分。
 
注意:如果您不熟悉事件循环,我建议您在进一步阅读之前先观看此视频。
 
调用堆栈是一个 LIFO(后进先出)结构,用于存储代码执行期间创建的执行上下文。简单来说,调用堆栈执行函数。
 
Web API是异步操作(获取请求、承诺、计时器)及其回调等待完成的地方。
 
任务队列(也称为宏任务)是一个 FIFO(先进先出)结构,它保存准备执行的异步操作的回调。例如,超时的回调setTimeout()——准备执行——被排入任务队列。
 
作业队列(也称为微任务)是一个 FIFO(先进先出)结构,用于保存准备执行的承诺回调。例如,已履行的承诺的解决或拒绝回调在作业队列中排队。
 
最后,事件循环会永久监控调用堆栈是否为空。如果调用堆栈为空,则事件循环会查看作业队列或任务队列,并将任何准备执行的回调出列到调用堆栈中。
 
3. 作业队列 vs 任务队列
 
我们再从事件循环的角度来看这个实验。我会一步一步分析代码执行。
 
A) 调用栈执行setTimeout(..., 0)并调度一个定时器。timeout()回调存储在Web API 中:
 
setTimeout(function timeout() {
 
console.log('Timed out!');
 
}, 0);
 
Promise.resolve(1).then(function resolve() {
 
console.log('Resolved!');
 
});
 
B) 调用堆栈执行Promise.resolve(true).then(resolve)并安排承诺解决方案。resolved()回调存储在Web API 中:
 
setTimeout(function timeout() {
 
console.log('Timed out!');
 
}, 0);
 
Promise.resolve(1).then(function resolve() {
 
console.log('Resolved!');
 
});
 
C)承诺立即解决,计时器立即超时。因此,定时器回调timeout()被排入任务队列,promise 回调resolve()被排入作业队列:
 
D)现在是有趣的部分:事件循环优先于任务出列作业。事件循环resolve()从作业队列中取出承诺回调并将其放入调用堆栈中。然后调用堆栈执行承诺回调resolve():
 
setTimeout(function timeout() {
 
console.log('Timed out!');
 
}, 0);
 
Promise.resolve(1).then(function resolve() {
 
console.log('Resolved!');
 
});
 
'Resolved!' 记录到控制台。
 
E) 最后,事件循环将计时器回调timeout()从任务队列中出列到调用堆栈中。然后调用堆栈执行定时器回调timeout():
 
setTimeout(function timeout() {
 
console.log('Timed out!');
 
}, 0);
 
Promise.resolve(1).then(function resolve() {
 
console.log('Resolved!');
 
});
 
'Timed out!' 记录到控制台。
 
调用堆栈为空。脚本执行已完成。
 
4. 总结
 
为什么立即解决的承诺比立即计时器处理得更快?
 
由于事件循环优先级使作业队列(存储已履行的承诺的回调)中的作业优先于任务队列(存储超时setTimeout()回调)中的任务。

如需转载,请注明文章出处和来源网址:http://www.divcss5.com/html/h64285.shtml