- Published on
最常见的事件循环 (Event Loop) 面试题目汇总
- Authors
- Name
- Tian Haipeng
基础题
console.log(1);
setTimeout(function () {
console.log(2);
}, 0);
Promise.resolve()
.then(function () {
console.log(3);
})
.then(function () {
console.log(4);
});
看完上面的代码,你觉得会打印出什么呢?答案在下方,一起来分析。
setTimeout
设置 0 毫秒,为什么会是 Promise
里面的内容先执行呢?原因是 Promise
会进入微任务队列,而 setTimeout
会进入宏任务队列。在一次事件循环中,宏任务一次只提取一个,因此 console.log(1)
后,会先检查微任务队列,不断提取到执行栈中直到微任务队列为空,所以这里会先执行 Promise
,然后才是 setTimeout
。
1;
3;
4;
2;
中阶题
console.log("begins");
setTimeout(() => {
console.log("setTimeout 1");
Promise.resolve().then(() => {
console.log("promise 1");
});
}, 0);
new Promise(function (resolve, reject) {
console.log("promise 2");
setTimeout(function () {
console.log("setTimeout 2");
resolve("resolve 1");
}, 0);
}).then((res) => {
console.log("dot then 1");
setTimeout(() => {
console.log(res);
}, 0);
});
看完上面的代码,你觉得实际执行后会打印出什么呢?让我们一起分析。
- 程序执行时首先会依顺序执行同步代码,因此会先打印
'begins'
。 - 遇到
setTimeout
时,它会被放入宏任务队列;接着遇到new Promise
,会先执行同步部分,打印'promise 2'
,然后再遇到一个setTimeout
,也会被放入宏任务队列。 - 主线程空闲后,开始检查宏任务队列,执行第一个
setTimeout
,打印'setTimeout 1'
,并将Promise.resolve()
放入微任务队列。 - 此时检查微任务队列,打印
'promise 1'
。 - 微任务队列空后,返回宏任务队列,执行第二个
setTimeout
,打印'setTimeout 2'
。 - 调用
resolve
后,进入.then
,打印'dot then 1'
,并将最后的setTimeout
放入宏任务队列。 - 最后宏任务队列执行,打印
resolve 1
。
"begins";
"promise 2";
"setTimeout 1";
"promise 1";
"setTimeout 2";
"dot then 1";
"resolve 1";
进阶题
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0);
async1();
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("promise2");
});
console.log("script end");
这道题加入了 async
语法。你觉得会打印出什么呢?让我们逐行分析。
- 程序首先执行同步代码,打印
'script start'
,然后将setTimeout
放入宏任务队列。 - 执行
async1
函数,打印'async1 start'
,调用await async2()
,打印'async2'
,注意await
后的代码会被放入微任务队列,因此不会立即打印'async1 end'
。 - 遇到
new Promise
,先打印'promise1'
,调用resolve
后,将.then
放入微任务队列。 - 最后打印
'script end'
。 - 执行微任务队列,先打印
'async1 end'
,然后打印'promise2'
。 - 微任务队列空了后,返回宏任务队列,执行
setTimeout
,打印'setTimeout'
。
"script start";
"async1 start";
"async2";
"promise1";
"script end";
"async1 end";
"promise2";
"setTimeout";
总结
通过以上三道题目,希望大家对事件循环的理解更加透彻!