首页 > 解决方案 > 事件循环、Promise 和 setTimeout

问题描述

考虑以下代码。我不太明白为什么它会这样工作。

  1. 当代码第一次运行时,它会遇到setTimeout,因此它会在 Web API(或其他地方)中注册它
  2. 然后它看到了希望并做同样的工作
  3. 然后它遇到了阻塞主线程2秒的while循环
  4. 然而,当这个 while 循环被执行时,Promise 已经被解析并放置在微任务队列中。同样,计时器已过期并放置在宏任务(回调)队列中。所以当 while 循环完成时,我们已经准备好执行 promise 和 timer。

我知道微任务队列具有更高的优先级,应该在宏任务队列的回调之前执行,但事实并非如此。请解释原因。

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();
}

标签: javascriptasynchronouspromisesettimeoutevent-loop

解决方案


链条的强度取决于其最薄弱的环节

你的第二个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”。


推荐阅读