事件循环机制

首先,JavaScript是一门单线程语言,在一个时间上只能做一件事情

那么我们我们可能有很多个事情(任务)需要去完成,这时候,JavaScript就把我们要完成的任务放在了任务队列,并且分为了同步任务异步任务

任务队列

所有任务都可以分为同步任务和异步任务。同步任务就是我们要立即执行的任务,一般会直接进入主线程执行。异步任务,就是可以异步执行的任务,比如网络资源请求,setTimeout这些,会通过任务队列( Event Queue )的机制来进行协调

img

同步任务进入主线程,异步任务进入任务队列。当主线程内的所有任务执行完毕后,会去任务队列里读取相应的任务推入主线程执行,这就是我们常说的Event Loop(事件循环)

每次事件循环关键步骤如下图所示:

clipboard.png

  • 首先判断JS是同步还是异步,同步就进入主线程,异步就进入event table
  • 异步任务在event table中注册函数,当满足触发条件后,被推入event queue
  • 同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主线程中

宏任务主要包含:script( 整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境)

微任务主要包含:Promise、MutaionObserver、process.nextTick(Node.js 环境)

说了这么多,目的就是能够让我们能够准确的判断出js执行结果顺序

看一个经典的例子

1
2
3
4
5
6
7
8
9
10
11
console.log(1)
setTimeout(function(){
console.log(2)
},0)
new Promise(function(resolve){
console.log(3)
resolve()
}).then(function(){
console.log(4)
})
console.log(5)

输出结果是1,3,5,4,2

那么为什么是这个结果呢?

首先整体代码作为宏任务进入主线程,遇到第一个console打印结果1,setTimeout作为宏任务进入宏任务队列等待,new promise 中的代码立即执行,输出 3, then作为微任务进入微任务队列等待,然后遇到第二个console打印5。至此,我们的一个个宏任务结束,目前任务队列情况如下

宏任务 微任务
setTimeout then

这时候我回到上面看看事件循环关键步骤图,宏任务执行完毕后,看看有没有能执行的微任务;很明显我们的then在微任务队列里,可以执行,输出结果4。此时,微任务队列没有可执行任务。开始下一次宏任务setTimeout,输出结果2。