JS中的Event Loop

218 阅读4分钟

JS执行机制的含义就是JS中代码的执行顺序,下面我们来分析JS代码的执行顺序。 JS的执行机制可以概括为一句话:

每执行完一次宏任务,就会执行清空一次微任务队列

问题来了:宏任务是什么,微任务是什么?

宏任务

宏任务包括同步任务和异步任务中的setTimeOut、setInterval;

微任务

微任务是除setTimeOut、setInterval之外的其他所有异步任务;

问题又来了:同步任务是什么,异步任务是什么?

同步任务

JS是一门单线程的语言,在主线程中执行的任务就是同步任务,同步任务在执行的时候,会形成一个执行栈;

异步任务

类似于ajax等需要等待回调结果的函数,无法在主线程中执行,而是进入Event Table(事件表),并在任务完成后,将回调函数注册进Event Queue(事件队列)中。像这种无法在主线程中立即执行,而是在Event Queue中注册回调函数的任务就是异步任务;

问题又又来了:“每执行完一次宏任务,就会清空一次微任务”到底是什么意思呢?

console.log('start');

setTimeout(() => {
   console.log('setTimeout01 start');
   
   new Promise((resolve) => {
       console.log('promise02');
       resolve();
   }.then(() => {
       console.log('promise02 then');
   }))
   
   setTimeout(() => {
       console.log('setTimeout02')
   },0)
   
   console.log('setTimeouto1 end')
},1000)

new Promise((resolve) => {
   console.log('promise01');
   resolve();
}.then(() => {
   console.log('promise01 then');
}))

console.log('end');

我们一起来分析一下上述代码的执行顺序:

第一轮事件循环

  • 首先整段代码作为宏任务进入主线程:

  • 遇到同步任务console.log,因此首先会打印“start”;
  • 然后来到异步任务setTimeout,待异步任务执行完成,将其回调函数注册进任务队列的宏任务队列;
  • 主线程中同步任务继续执行,执行到Promise,首先会打印“promise01”;
  • 然后将Promise的then注册进任务队列的微任务队列;
  • 继续执行,打印"end"; 至此,主线程中的宏任务执行完成;
  • “宏任务执行完成,清空任务队列中的微任务”。因此来到微任务队列,本着清空的目的开始执行队列中的所有回调函数。由于此微任务队列中只有promise01,直接执行代码,打印‘promise01 then’;
  • 截止目前,第一轮事件循环结束,我们回顾下整个过程:
    • 整段代码作为一个宏任务进入主线程,首先遇到console,打印‘start’;
    • 遇到setTimeout,待执行完成入任务队列;
    • 遇到promise,打印‘promise01’,then入任务队列;
    • 遇到console,打印‘end’;
    • 宏任务执行完成,开始清空微任务队列
    • 只有promise01,执行代码,打印‘promise01 then’。
  • 第一轮打印结果:start -- promise01 -- end -- promise01 then;

第二轮事件循环

  • 来到宏任务队列,取出宏任务队列队头的函数setTimeout01,将其‘放入’主线程开始执行;
  • 遇到console,打印‘setTime01 start’;
  • 遇到promise,打印‘promise02’,then注册进任务队列的微任务队列;
  • 遇到setTimeout,进任务队列的宏任务队列;
  • 遇到console,打印‘setTime01 end’;至此,主线程中的宏任务执行完成;
  • 开始清空微任务队列,只有一个promise02,因此执行代码,打印‘promise02 then’;
  • 截至目前,第二轮事件循环结束,我们回顾下整个过程:
    • setTimeout01作为一个宏任务进入主线程,首先遇到console,打印‘setTime01 start’;
    • 遇到setTimeout02,待执行完成入任务队列;
    • 遇到promise02,打印‘promise02’,then入任务队列;
    • 遇到console,打印‘setTime01 end’;
    • 宏任务执行完成,开始清空微任务队列
    • 只有promise02,执行代码,打印‘promise02 then’。
  • 第二轮打印结果:setTime01 start -- promise02 -- setTime01 end -- promise02 then;

第三轮事件循环

  • 来到宏任务队列,取出宏任务队列队头的函数setTimeout02,将其‘放入’主线程开始执行;
  • 遇到console,打印‘setTimeout02’;至此,宏任务执行完成;
  • 开始清空微任务队列,发现此时微任务队列为空;
  • 因此,第三轮事件循环结束;

第四轮事件循环

  • 首先来到宏任务队列,发现也为空,因此整个程序执行完成;

总结

  • 同步任务在主线程中执行,异步任务需要进Event Table(事件表),待执行完成注册回调函数进Event Queue(事件队列);
  • 从宏任务和微任务角度理解JS机制,宏任务包括同步任务和异步任务中的定时器,微任务是异步任务除定时器之外的部分;
  • 通过分析上面的代码,我们发现JS的执行机制可以概括为:每执行完一次宏任务,就会清空一次微任务队列
以上是对JS执行机制的学习记录,如有误还请指出