事件循环

众所周知的javascript是一门单线程语言,而事件循环(Event Loop)就是js为了协调事件,交互,渲染,网络等的执行机制。

任务队列

既然是单线程,那么为了防止一个任务耗时过长,导致页面渲染卡死等问题,我们就可以将任务区分为

  1. 同步任务(synchronous),同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行下一个任务,
  2. 异步任务(asynchronous),异步任务指的是,不进入主线程、而进入“任务队列”任务,只有“任务队列”通知主线程。某个异步任务可以执行了,该任务才会进入主线程执行。

我们看图说话:
image.png

  1. 同步和异步任务分别进入不同的执行”场所”,同步的进入主线程,异步的进入Event Table并注册函数。
  2. 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
  3. 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
  4. 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

执行顺序

除了同步任务和异步任务,我们对任务有更精细的定义:

  • macrotask主要包含:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(node)
  • microtask主要包含:Promise.then、MutaionObserver、process.nextTick(node)

不同类型的任务会进入对应的Event Queue,比如setTimeoutsetInterval会进入相同的Event Queue。
事件循环的顺序决定了js的执行顺序,执行顺序如下图所示:

15fdcea13361a1ec.jpg
我们可以清楚的看到优先执行宏任务,然后查看是否有可执行的微任务,如果有执行微任务,然后开始新的宏任务,依次循环执行。

代码分析

任务源 -> 微任务 -> 宏任务

  1. console.log('start')
  2. setTimeout( function () { // 宏任务
  3. console.log('setTimeout')
  4. }, 0 )
  5. Promise.resolve().then(function() { // 微任务
  6. console.log('promise1');
  7. }).then(function() { // 微任务
  8. console.log('promise2');
  9. });
  10. console.log('end')
  11. // start
  12. // end
  13. // promise1
  14. // promise2
  15. // setTimeout
  16. 0. 代码作为宏任务开始执行
  17. 1. 执行console.log('start')
  18. 2. setTimeout将其回调函数注册到宏任务Event Quene
  19. 3. promise执行,then函数分发到微任务Event Quene
  20. 4. 执行console.log('end')
  21. 5. 有可执行微任务执行console.log('promise1'), console.log('promise2')
  22. 6. 开始新的事件循环,发现setTimeout的宏任务回调函数,立即执行