面试官(为什么|面试官:为什么 Promise 比setTimeout() 快())
作者:Milos Protic
译者:前端小智
来源:devinduct
有梦想,有干货,微信搜索 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。1.实验
本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。
我们来做个实验。哪个执行得更快:立即解决的 Promise 还是立即
setTimeout
(也就是0毫秒的setTimeout)?Promise.resolve(1).then(function resolve() {
console.log('Resolved!');
});
setTimeout(function timeout() {
console.log('Timed out!');
}, 0);
// 'Resolved!'
// 'Timed out!'
promise.resolve(1)
是一个静态函数,它返回一个立即解析的promise
。setTimeout(callback, 0)
以0毫秒
的延迟执行回调函数。我们可以看到先打印
'Resolved!'
,再打印Timeout completed!
,立即解决的 promise 比立即setTimeout
更快。是因为
Promise.resolve(true).then(...)
在setTimeout(..., 0)
之前被调用了,所以 Promise 过程会更快吗? 公平的问题。所以,我们稍微更改一下实验条件,然后先调用
setTimeout(..., 0)
:setTimeout(function timeout() {
console.log('Timed out!');
}, 0);
Promise.resolve(1).then(function resolve() {
console.log('Resolved!');
});
// 'Resolved!'
// 'Timed out!'
setTimeout(..., 0)
在Promise.resolve(true).then(...)
之前被调用。但,还是先打印Resolved!
在打印'Timed out!'
。这是为啥呢?
2.事件循环
与异步 JS 相关的问题可以通过研究事件循环来回答。我们回顾一下异步 JS 工作方式的主要组成部分。
![面试官(为什么|面试官:为什么 Promise 比setTimeout() 快())](https://img.it610.com/image/info9/7ed6502c58384458930fc7a52e786a98.jpg)
文章图片
调用堆栈是一个LIFO(后进先出)结构,它存储在代码执行期间创建的执行上下文。简单地说,调用堆栈执行这些函数。
Web api是异步操作(fetch 请求、promise、计时器)及其回调等待完成的地方。
task queue (任务队列)是一个FIFO(先进先出)结构,它保存准备执行的异步操作的回调。例如,超时的
setTimeout()
的回调函数或准备执行的单击按钮事件处理程序都在任务队列中排队。job queue (作业队列)是一个FIFO(先入先出)结构,它保存准备执行的
promise
的回调。例如,已完成的承诺的resolve
或reject
回调被排在作业队列中。最后,事件循环永久监听调用堆栈是否为空。如果调用堆栈为空,则事件循环查看作业队列或任务队列,并将准备执行的任何回调分派到调用堆栈中。
3.作业队列与任务队列
我们从事件循环的角度来看这个实验,我将对代码执行进行一步一步的分析。
A)调用堆栈执行
setTimeout(..., 0)
并计划一个计时器, timeout()
回调存储在Web API中:![面试官(为什么|面试官:为什么 Promise 比setTimeout() 快())](https://img.it610.com/image/info9/91f3c0c843594caba296a60d0c08d185.jpg)
文章图片
![面试官(为什么|面试官:为什么 Promise 比setTimeout() 快())](https://img.it610.com/image/info9/6ed127562ae347f2adf5f068368141bb.jpg)
文章图片
B)调用堆栈执行
Promise.resolve(true).then(resolve)
并安排一个 promise
解决方案。 resolved()
回调存储在Web API中:![面试官(为什么|面试官:为什么 Promise 比setTimeout() 快())](https://img.it610.com/image/info9/bb9ba6316fec494f9fc8021630fe1020.jpg)
文章图片
![面试官(为什么|面试官:为什么 Promise 比setTimeout() 快())](https://img.it610.com/image/info9/a148f37b089c4a5a87a8f99eef9ea52c.jpg)
文章图片
C)promise 立即被解析,同时计时器也立即执行。这样,定时器回调
timeout()
进入任务队列,promise
回调resolve()
进入作业队列![面试官(为什么|面试官:为什么 Promise 比setTimeout() 快())](https://img.it610.com/image/info9/3b17d513e7a54a6398505df7ddf2710a.jpg)
文章图片
D)现在是有趣的部分:作业队列(微任务)优先级高于任务队列(宏任务)。 事件循环从作业队列中取出promise回调
resolve()
并将其放入调用堆栈中。 然后,调用堆栈执行promise回调resolve()
:![面试官(为什么|面试官:为什么 Promise 比setTimeout() 快())](https://img.it610.com/image/info9/a0bccc47ee54469dae91702bfe66dabf.jpg)
文章图片
E)最后,事件循环将计时器回调
timeout()
从任务队列中出队到调用堆栈中。 然后,调用堆栈执行计时器回调timeout()
:![面试官(为什么|面试官:为什么 Promise 比setTimeout() 快())](https://img.it610.com/image/info9/ff25c546a8cc48318b5d73c15a2a53b9.jpg)
文章图片
调用堆栈为空,已完成脚本的执行。
总结
为什么立即解决的 promise 比立即执行定时器处理得更快?
由于事件循环优先级的存在,因此与任务队列(存储超时的
setTimeout()
回调)相比,作业队列(用于存储已实现的Promise
回调)的优先级更高。完~ 我是小智,我要去刷碗了,我们下期见!
代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文: https://dmitripavlutin.com/ja...
交流
有梦想,有干货,微信搜索 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。【面试官(为什么|面试官:为什么 Promise 比setTimeout() 快())】
本文 GitHub https://github.com/qq44924588... 已收录,有一线大厂面试完整考点、资料以及我的系列文章。
![面试官(为什么|面试官:为什么 Promise 比setTimeout() 快())](https://img.it610.com/image/info9/8a4e9c23d6b14b89b47f399b49cce3ba.jpg)
文章图片
推荐阅读
- 为什么你的路演总会超时()
- 财商智慧课(六)
- 吃了早餐,反而容易饿(为什么?)
- 为什么越花钱的人越有钱,越舍不得花钱的人却越穷()
- dubbo基本认识
- 为什么985/211的学生能胜任工作获得老板的青睐。
- 年轻人,干了孤独这杯酒
- 为什么孩子一定要学会可视化思维!
- ts泛型使用举例
- 康恩赫助力线头公益#义卖星推官#,分享你最喜爱的义卖明星