事件循环机制
首先,JavaScript是一门单线程语言,在一个时间上只能做一件事情
那么我们我们可能有很多个事情(任务)需要去完成,这时候,JavaScript就把我们要完成的任务放在了任务队列,并且分为了同步任务和异步任务
任务队列
所有任务都可以分为同步任务和异步任务。同步任务就是我们要立即执行的任务,一般会直接进入主线程执行。异步任务,就是可以异步执行的任务,比如网络资源请求,setTimeout这些,会通过任务队列( Event Queue )的机制来进行协调
同步任务进入主线程,异步任务进入任务队列。当主线程内的所有任务执行完毕后,会去任务队列里读取相应的任务推入主线程执行,这就是我们常说的Event Loop(事件循环)
每次事件循环关键步骤如下图所示:
- 首先判断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执行结果顺序
看一个经典的例子
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 |
务 | 微任务 |
---|---|
setTimeout | then |
这时候我回到上面看看事件循环关键步骤图,宏任务执行完毕后,看看有没有能执行的微任务;很明显我们的then在微任务队列里,可以执行,输出结果4。此时,微任务队列没有可执行任务。开始下一次宏任务setTimeout
,输出结果2。