概述
JavaScript
属于单线程语言(除了 web work)。单线程,必然导致了代码需要一行行的去执行,但是一些耗时的操作(http 请求等),如果还是使用同步的方式,必然会导致页面阻塞,所以这个时候就需要异步操作。在JavaScript
中,处理这种异步操作的机制就是事件循环(Event Loop)
。
Event Loop
执行逻辑
JavaScript
从上到下开始执行代码,当遇到异步任务时,将异步任务放到任务队列中,当整个文件的代码都执行完成后(也可以理解成执行栈中的代码都执行成),则开始从任务队列(不一定是先入先出)中取任务执行,任务队列中的任务都执行完成后,这个过程(从任务队列中取任务)依然不停止,只是进行等待,直到下一个任务的到来,整个过程就叫事件循环(Event Loop)
。
console.log(111);
setTimeout(() => {
console.log(222);
}, 0)
console.log(333);
// 执行结果
// 111
// 333
// 222
从上面的代码就可以看到,哪怕 setTimeout
设置的延时时间为 0, 其中的方法也是先被放在任务队列中,等所有的代码都执行完成了,才从任务队列中取出并执行。
任务队列
任务对队列分为
宏任务
和微任务
两类,其中微任务
执行的优先级高于宏任务
。
- js 执行任务时,有个原则就是,不会停止执行当前任务,不管什么优先级的任务进来,也会将当前任务执行完;
- 在
Event Loop
期间,当从任务队列取事件时,优先取微任务
中的数据,当微任务
中的数据执行完成后,再取宏任务
中的数据;需要多说一点的是,早期的js
没有微任务
,但是后来觉得需要有优先级,如setTimeout
这种不紧要的异步,可以放在后面执行,这块就是说明了从任务队列取数据不一定是先入先出
。
宏任务
script
(可以理解为外层同步代码);setTimeout/setInterval
;UI rendering/UI事件
;postMessage,MessageChannel
;setImmediate,I/O(Node.js)
.
微任务
可以说,微任务都是比较新的一些功能(相对宏任务来说)。
Promise
;process.nextTick(Node.js)
;Object.observe
(已废弃;Proxy 对象替代);MutaionObserver
示例
- 先执行了
console.log(111)
; setTimeout
属于异步操作,添加到任务队列(宏任务);- 构造函数
Promise
中的代码属于同步任务,就算Promise
中的代码没有异步操作,then
部分的代码,依然会被扔到任务队列(微任务)中; - 执行
console.log(555)
; - 由于
微任务
的优先级高于宏任务
,所以先执行.then
部分的代码,打印444
; 微任务
中的代码都执行完成后,在执行宏任务
中的代码,打印console.log(222)
;
console.log(111);
setTimeout(() => {
console.log(222);
}, 0);
new Promise((resolve) => {
console.log(333);
resolve();
})
.then(() => {
console.log(444);
});
console.log(555);
// 执行结果
// 111
// 333
// 555
// 444
// 222