高频面试题 -- Event Loop

1,910 阅读3分钟

前言

在最近的滴滴面试中都被问到 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 称为 jobsmacrotask 称为 task 宏任务是由宿主发起的,而微任务由JavaScript自身发起。

宏任务微任务
具体事件1. script 代码
2.setTimeout/setInterval
3.setImmediate (Node独有)
4.requestAnimationFrame (浏览器独有)
5.UI rendering (浏览器独有)
1.process.nextTick (Node独有)
2.Promise
3.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
MicrotasksPromise then
console.log: 1 5  7

先执行同步代码(11217行),将setTimeout 放入宏任务队列,Promise then 放入微任务队列。

第二次执行

Tasks: run script、 setTimeout callback
Microtasks: null
console.log: 6

检测微任务队列不为空,执行Promise then

第三次执行

TaskssetTimeout callback

MicrotasksPromise then
console.log: 2 3

执行宏任务队列 setTimeOut,检测到 Promise then ,放入微任务队列

第四次执行

Tasksnull

Microtasksnull
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 LoopWeb前端一个非常重要且特色的功能。做Web前端的,或多或少都对它有一定的了解。算是入门必备知识了。个人写的不是很全面,欢迎大家在评论区指正。

↓↓↓

我是寒竹,一名大三学生,正在准备春招实习,欢迎大家和我一起学习,相互监督。
wx: yf1919138974