当面试官问你事件循环,他想听你答什么?

780 阅读4分钟

事件循环是js的一种执行机制;

提到事件循环首先要讲知道js的执行机制是怎么样的,才能清楚怎么算一个事件循环;

js的执行机制:

js是一门单线程语言,它的执行顺序是根据出现的顺序依次执行的。但是这样就会有出现一个问题,比如想打开一个有图文的页面,如果页面里的图片很大,图片一直没有加载出来,后面的文字也不能显示,只能等着图片加载出来之后再去加载,这显然不合理啊,不合理就会推动改变。为了解决这个问题,就有了同步任务/异步任务这个概念。

需要注意的是,这里的同步任务和异步任务的定义并不是真正意义上的‘多线程’,而是通过单线程模拟出来的

同步/异步的执行逻辑如下图所示:

image.png

在我理解,同步任务/异步异步,其实就是同步的就直接放到主线程里去执行,异步的就是放到异步这个队列里,等待主线程执行完之后去执行。一个最简单的例子,比如:

setTimeout(function(){
  console.log(1);
},2000)
console.log(2);

按照js执行机制,应该先执行setTimeout,但是setTimeout是一个异步任务,进入执行栈之后先被放到异步队列Event Table中并注册回调函数,2s后将它的回调函数移入Event Queue中,等待同步任务中的任务执行完之后再来执行; 继续往下走遇到了console,它是个同步任务,直接放进主线程中执行; 执行完console后,当前主线程没有其他任务,开始执行setTimeout的注册函数; 因此结果为2,1;

这里又要引出两个新的概念:宏任务和微任务:

宏任务:包括整体代码script,setTimeout,setInterval;

微任务:Promise,process.nextTick;

image.png

一个宏任务执行完之后,会去执行当前排在队列中的微任务,等微任务执行完之后,会再去执行新的宏任务,循环往复直到全部任务都执行完;

刚刚举得是一个最简单的例子,它就是一个事件循环,但是实际场景往往复杂很多,比如:

console.log(1);
setTimeout(function(){
  console.log(2);
})
new Promise(function(resolve){
    console.log(3);
    resolve();
}).then(function() { 
    console.log(4); 
})
setTimeout(function(){
  console.log(5);
  new Promise(function(resolve){
    console.log(6);
    resolve();
  }).then(function() { 
        console.log(7); 
  })
})
console.log(8);

这段的执行逻辑是:

  1. 从一个整段代码script开始作为一个‘宏任务1’进入主线程,遇到console.log(1),输出1
  2. setTimeout的回调函数是一个宏任务,暂时存放到Event Queue中,记作‘宏任务2’;
  3. new Promise直接执行,输出3,then被分发到微任务Event Queue中,记作‘微任务1’;
  4. setTimeout的回调函数是一个宏任务,分发到Event Queue中,记作‘宏任务3’;
  5. console.log(8),输出8,到这里,‘宏任务1’执行完了,开始执行微任务;
  6. 发现了一个微任务‘微任务1’,输出4
  7. 此时第一轮宏任务‘宏任务1’执行完了,第一轮的微任务‘微任务1’也执行完了,到这里就结束了一个事件循环,开始第二个事件循环;
  8. 开始一个新的宏任务,发现有个‘宏任务2’,输出2
  9. 此时第二轮宏任务‘宏任务2’执行完了,没有微任务,开启新一轮的宏任务;
  10. 开启新一轮的宏任务‘宏任务3,遇到了console,输出5
  11. new Promise直接执行,输出6,then被分发到微任务Event Queue中,记作‘宏3-微任务1’;
  12. 开始执行微任务‘宏3-微任务1’,输出7

至此,全部任务都执行完了,输出结果为:1,3,8,4,2,5,6,7

讲到这里,对于事件循环一个浅显的介绍就讲完了,还有很多细节的点没有提到,有兴趣可以再去了解。

可以说,在面试中,面试官问事件循环,他最想听的其实是你对于js执行机制的一个理解,因为解释事件循环就得解释js的执行机制,了解了js的执行机制,知道js的代码是如何执行的,才能知道什么算一个事件循环。