【问题标题】:Event loop, Promise and setTimeout事件循环、Promise 和 setTimeout
【发布时间】:2021-07-31 02:10:51
【问题描述】:
考虑以下代码。我不太明白为什么会这样。
- 当代码第一次运行时,它会遇到
setTimeout,因此它会在 Web API(或其他地方)中注册它
- 然后它看到了承诺并做同样的工作
- 然后遇到阻塞主线程2秒的while循环
- 但是,当这个 while 循环执行时,Promise 已被解析并放入微任务队列中。同样,计时器已过期并放置在宏任务(回调)队列中。所以当 while 循环完成时,我们准备好执行承诺和计时器。
我知道微任务队列具有更高的优先级,应该在宏任务队列的回调之前执行,但事实并非如此。请解释原因。
setTimeout(() => {
console.log("Timer");
}, 1000);
let p = new Promise(res => {
setTimeout(() => {
res("Promise");
}, 1500);
});
p.then(msg => console.log(msg));
let start = new Date().getTime();
let stop = start;
while (stop !== start + 2000){
stop = new Date().getTime();
}
【问题讨论】:
标签:
javascript
asynchronous
promise
settimeout
event-loop
【解决方案1】:
链条的强度取决于其最薄弱的环节
您的第二个setTimeout 确实在您期望的时候开始,但是直到 JS 去寻找它是否已经完成,它才能正式完成。寻找 setTimeout 是否已完成的过程,正式名称为macrotask。承诺解决方案(之后)是一个微任务。
因此,要解决该承诺需要发生两件事:由 JS 审查的 setTimeout 计时器,以及要解决的承诺。前者是你的瓶颈。
setTimeout(() => {
console.log("Timer");
}, 1000);
let p = new Promise(res => {
setTimeout(() => {
res("Promise");
}, 1500);
console.log("setTimeout #2 has begun ticking")
});
p.then(msg => console.log(msg));
let start = new Date().getTime();
let stop = start;
while (stop !== start + 2000){
stop = new Date().getTime();
}
我们通过缩短第二次setTimeout的时间来测试一下
setTimeout(() => {
console.log("Timer");
}, 1000);
let p = new Promise(res => {
setTimeout(() => {
res("Promise");
}, 500);
console.log("setTimeout #2 has begun ticking")
});
p.then(msg => console.log(msg));
let start = new Date().getTime();
let stop = start;
while (stop !== start + 2000){
stop = new Date().getTime();
}
第二个程序在“Timer!”之前打印了“Promise”。
【解决方案2】:
- 带有“计时器”的
setTimeout() 计划在 1 秒后执行。我们称之为 timer1。
- 一个promise 被创建,executor 函数运行另一个
setTimeout(),它将在1.5 秒内解决promise。我们称之为 timer2。
-
.then() 添加了一个回调以在 promise 解决后运行。我们称之为 promiseCallback。
- 循环开始,将运行 2 秒。
-
timer1 应该被执行,但线程正忙于执行循环。 timer1 通过将其添加到宏任务队列来延迟,直到线程不忙为止。
-
timer2 应该被执行,但线程正忙于执行循环。 timer2 通过将其添加到宏任务队列来延迟直到线程不忙。
- 循环结束。线程不再忙了。
- 微任务队列为空,因此接下来要运行的是宏任务。
-
timer1 运行。它打印
"Timer"。
- 微任务队列为空,因此接下来要运行的是宏任务。
-
timer2 运行。它使用值
"Promise" 解析承诺。
- 微任务队列有一个任务,所以接下来要运行它。
-
promiseCallback 运行。它打印出承诺解决的值:
"Promise"。