# 事件循环(Event Loop)

js是一门单线程语言,所有任务按照顺序依次执行。不管是什么新框架新语法糖实现的所谓异步,其实都是用同步的方法去模拟的。Event Loop是javascript的执行机制。

# js执行机制

如图,js执行任务时,同步任务会被推到主线程中执行,异步任务推到事件队列(Event Queue)当中排队等待执行,当主线程同步任务执行完毕后,再把事件队列(Event Queue)推到主线程执行。主线程会不断读取事件队列,依次执行这些事件所对应的函数。

所以每个异步函数都有一个回调函数,等待主线程执行完后调用。

像setTimeout、setInterval、ajax这些异步操作,都会被推到事件队列中,等待执行。

# 宏任务与微任务

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

上面代码执行结果为 1、3、5、4、2 setTimeout和promise同样是异步,为什么setTimeout先添加的却不能先执行呢,这就涉及到宏任务和微任务。

  • 宏任务(macro-task): script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)
  • 微任务(micro-task): 原生Promise(有些实现的promise将then方法放到了宏任务中)、process.nextTick、Object.observe(已废弃)、 MutationObserver 记住就行了。

从脚本开始script做为当前宏任务在主线程中执行,当同步任务执行完后,清空主线程,然后把等待执行的异步任务推到主线程执行,但首先找当前宏任务下的微任务来执行。当前宏任务下的微任务(异步任务)执行完毕后,清空主线程,再执行另一个宏任务。

事件循环(Event Loop):每次执行一个宏任务,同步任务按顺序执行,异步任务推送到事件队列等待执行,同步执行完后,取出当前宏任务下的微任务执行,直到微任务队列为空,完成循环,执行下一个宏任务。

事件循环包含一个宏任务和多个微任务。

在事件循环执行中,不同的异步任务会被推到不同的事件队列中,如promise会被推到微任务事件队列中,setTimeout会被推到宏任务事件队列中。

console.log(1);

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

setTimeout(function() {
  console.log(9);
  process.nextTick(function() {
    console.log(10);
  })
  new Promise(function(resolve) {
    console.log(11);
    resolve();
  }).then(function() {
    console.log(12)
  })
})

console.log(13)
  • 第一轮,从script宏任务开始第一轮循环,执行同步,new Promise立刻执行,输出 1、7、13。异步process.nextTick和then依次推送到微任务事件队列中,setTimeout依次推送到宏任务队列中。再接着主线程寻找当前宏任务下的微任务,输出6、8.

  • 第二轮,从第一个setTimeout宏任务开始,依次输出2、4、3、5

  • 第三轮,从第二个setTimeout宏任务开始,依次输出9、11、10、12

所以上面代码共进行三次循环,输出结果为: 1,7,13,6,8,2,4,3,5,9,11,10,12。