前言
在最近的滴滴面试中都被问到 Event Loop相关问题,觉得应该输出一下,从而更好整理自己思路,废话不多说,先上一道面试题:
console.log('1');
setTimeout(function() {
console.log('2');
new Promise(function(resolve) {
console.log('3');
resolve();
}).then(function() {
console.log('4')
})
})
new Promise(function(resolve) {
console.log('5');
resolve();
}).then(function() {
console.log('6')
})
console.log('7')
如果不会的小伙伴可以先往下看,了解js执行机制之后再回来看这道题。
Event Loop是什么
event loop是一个执行模型,在不同的地方有不同的实现。浏览器和NodeJS基于不同的技术实现了各自的Event Loop。
- 浏览器的
Event Loop是在 html5 的规范中明确定义 - NodeJS的
Event Loop是基于libuv实现的。
MacroTask(宏任务)和 MicroTask(微任务)
ES6规范中,microtask称为jobs,macrotask称为task宏任务是由宿主发起的,而微任务由JavaScript自身发起。
| 宏任务 | 微任务 | |
|---|---|---|
| 具体事件 | 1. script 代码 2. setTimeout/setInterval3. setImmediate (Node独有) 4. requestAnimationFrame (浏览器独有) 5. UI rendering (浏览器独有) | 1.process.nextTick (Node独有) 2. Promise3. Object.observe 4. Object.observe(已废弃;Proxy 对象替代) |
| 执行顺序 | 后执行 | 先执行 |
浏览器的 Event Loop
Event Loop流程图
事件循环的进程模型
Event Loop中,每一次循环称为tick,每一次tick的任务如下:
- 选择当前要执行的任务队列,选择任务队列中最先进入的任务,如果任务队列为空即
null,则执行跳转到微任务(MicroTask)的执行步骤。 - 将事件循环中的任务设置为已选择任务
- 执行任务
- 事件循环中当前运行任务设置为
null - 将已经运行完成的任务从任务队列中删除
microtasks步骤:进入microtask检查点- 更新界面渲染
- 返回第一步
执行栈在执行完同步任务后,查看执行栈是否为空,如果执行栈为空,就会去执行
Task(宏任务),每次宏任务执行完毕后,检查微任务(microTask)队列是否为空,如果不为空的话,会按照先入先出的规则全部执行完微任务(microTask)后,设置微任务(microTask)队列为null,然后再执行宏任务,如此循环
问题解答
打印的顺序为:1 5 7 6 2 3 4
解析:
宏任务(
Tasks)、微任务(Microtasks)
第一次执行
Tasks: run script、setTimeout callback
Microtasks:Promise then
console.log: 1 5 7
先执行同步代码(1,12,17行),将setTimeout 放入宏任务队列,Promise then 放入微任务队列。
第二次执行
Tasks: run script、 setTimeout callback
Microtasks: null
console.log: 6
检测微任务队列不为空,执行Promise then
第三次执行
Tasks:setTimeout callback
Microtasks:Promise then
console.log: 2 3
执行宏任务队列 setTimeOut,检测到 Promise then ,放入微任务队列
第四次执行
Tasks:null
Microtasks:null
console.log: 4
执行微任务队列中的 Promise then,此时Event Loop执行完成
测试题
练几道Event Loop 面试题自测一下吧
第一题
setTimeout(function () {
console.log('1');
})
new Promise(function (resolve) {
console.log('2');
for (var i = 0; i < 1000; i++) {
i == 99 && resolve();
}
console.log('3');
}).then(function () {
console.log('4');
})
console.log('5')
第二题
console.log('start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
new Promise((resolve) => {
console.log('promise')
resolve()
})
.then(() => {
console.log('then1')
})
.then(() => {
console.log('then2')
})
console.log('end')
第三题
console.log('start')
setTimeout(() => {
console.log('timer1')
Promise.resolve().then(function() {
console.log('promise1')
})
}, 0)
setTimeout(() => {
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')
})
}, 0)
Promise.resolve().then(function() {
console.log('promise3')
})
console.log('end')
总结
Event Loop是Web前端一个非常重要且特色的功能。做Web前端的,或多或少都对它有一定的了解。算是入门必备知识了。个人写的不是很全面,欢迎大家在评论区指正。
↓↓↓
我是寒竹,一名大三学生,正在准备春招实习,欢迎大家和我一起学习,相互监督。
wx: yf1919138974