结合阮一峰大神和网上其他写event-loop文章再总结一下自己的对event-loop的理解
Event Loop定义
-
Event loop是一个程序结构,用于等待,发送消息和事件,简单地说就是在程序设置两个线程,一个负责程序本身运行,一个负责程序与其他进程的通信,可称为“event-loop”线程
-
在浏览器和NodeJS都实现了各自的Event Loop
宏队列和微队列
队列采用先进先出方式
- 宏队列(macrotask) 一些异步任务的回调会进入宏队列,如:
- setTimeout
- setInterval
- setImmediate (Node独有)
- requestAnimationFrame (浏览器独有)
- I/O
- UI rendering (浏览器独有)
- 微队列(microtask) 一些异步任务的回调会进入微队列,如:
- process.nextTick (Node独有)
- Promise.then
- Object.observe
- MutationObserver
- 调用栈(补充) 用于存放全局Script代码
浏览器Event Loop执行方式
- 顺序执行全局Script代码,遇到同步语句则执行,遇到异步语句则把它丢到宏队列或微队列。
如遇到console.log则正常执行; 遇到setTimeout则把他扔进宏队列; 遇到promise,则先正常执行promise里面语句,把后面的then回调函数扔进微队列
- 全局Script代码执行完后,调用栈为空
- 再从微队列取出位于队首代码或回调函数,放入调用栈中执行,此过程中如果产生新的微队列或宏队列函数则照常扔到队列末尾
如执行promise.then()
- 重复取出微队列函数,直到微队列为空
- 再从宏队列取出一个函数放入调用栈执行
- 重复1~5直到宏队列为空
例子
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3)
});
});//macrotask1
new Promise((resolve, reject) => {
console.log(4)
resolve(5)
}).then((data) => {
console.log(data);
})
setTimeout(() => {
console.log(6);
})//macrotask2
console.log(7);
- 执行
console.log(1)
输出1 - setTimeout扔进宏队列,将这个记作macrotask1,此时宏队列为{macrotask1}
- 同步执行promise内语句
console.log(4)
,输出4,后面的then
扔进微队列,此时的微队列{console.log(5)} - 下一个setTimeout扔进宏队列,记作macrotask2,此时的宏队列{macrotask1,macrotask2}
- 执行
console.log(7)
输出7 - 从微队列取出队首
console.log(5)
执行,输出5,此时微队列为{} - 由于微队列为空,于是从宏队列取出队首函数macrotask1,扔进调用栈执行,此时宏队列{macrotask2}
- 执行macrotask1,执行
console.log(2)
输出2,后面的Promise.resolve().then()
扔进微队列,此时微队列{console.log(3)} - macrotask1执行完(调用栈空),再去执行微队列{console.log(3)},输出3,此时微队列{}
- 微队列空,再从宏队列取出函数macrotask2,执行
console.log(6)
,输出6 - 宏队列空,代码执行完毕
- 最终输出为1475236
接下来在看一个包含async函数的例子
async function mission1(){
await console.log(1)
return new Promise((resolve)=>{
resolve(console.log(2))
})
}
setTimeout(()=>console.log(3),0)
mission1().then((res)=>console.log(4))
console.log(5)
输出结果:1 5 2 4 3
怎么说呢,在上文中并没有提到async函数在eventloop里面扮演的是一个什么角色,其实你可以这样理解,你可以把整一个async函数分成两部分,一部分是await前面的代码,一部分是await后面的代码,await前面的代码正常运行就可以了,await后面(包括右边)的代码可以看成一整个promise,于是,await右边的代码就先执行,然后在await后面的代码就放进微队列里面,就如同放入了then回调函数里面一样