Event Loop
Event Loop 是一个执行模型,浏览器和 NodeJS 基于不同的技术实现了各自的 Event Loop
- 浏览器的 Event Loop 再 H5 中明确定义
- NodeJS 的 Event Loop 是基于 libuv 实现,可参考node 官方文档
宏队列和微队列
宏队列 mcacrotask
也叫 tasks。一些异步任务的回调会依次进入 macro task queue,等待后续被调用
- setTimeout
- setInterval
- setImmediate(Node 独有)
- requestAnimationFrame(浏览器独有)
- I/O
- UI rendering(浏览器独有)
微队列 microtask
也叫 jobs。另一些异步任务的回调以此进入 micro task queue,等待后续被调用
- process.nextTick(Node 独有)
- Promise
- Object.observe
- Mutatineobserve
浏览器的 Event Loop
执行 Javascript 的具体流程
- 执行全局 Script 同步代码,这些同步代码有一些是同步语句,有一些是异步语句
- 全局 Script 代码执行完毕后,调用栈 Stack 会清空
- 从
微队列(microtask queue)
列去除位于队首的任务,放入调用栈 Stack 中执行,执行完毕后微队列
长度减 1 - 如果在执行
微队列
过程中,又产生了新任务,则该任务放入队列的队尾。 微队列
中所有任务都执行完毕,此时微队列
为空,调用栈 Stack 也为空- 取出
宏队列(macrotask queue)
中位于队首的任务,放入 Stack 中执行 - 执行完成后,调用栈 Stack 为空
- 重复 3 ~ 7 步骤
三个重点
宏队列
一次只从队列中取出一个任务执行,执行完成后就执行微队列
队列中的任务;微队列
中所有任务都会被取出来,直到队列为空- 上图没有 rendering 节点,在执行完
微队列
之后,下一个宏队列
之前,紧跟执行 UI render
习题练习
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3);
});
});
new Promise((resolve, reject) => {
console.log(4);
resolve(5);
}).then((data) => {
console.log(data);
});
setTimeout(() => {
console.log(6);
});
console.log(7);
执行过程
Step 1
console.log(1)
- Stack Queue : [console]
- Macrotask Queue: []
- Microtask Queue: []
- 打印结果
1
Step 2
setTimeout(() => {
// 这个回调函数叫做callback1,setTimeout属于macrotask,所以放到macrotask queue中
console.log(2);
Promise.resolve().then(() => {
console.log(3);
});
});
- Stack Queue: [setTimeout]
- Macrotask Queue: [callback1]
- Microtask Queue: []
- 打印结果
1
Step 3
new Promise((resolve, reject) => {
// 注意,这里是同步执行的,如果不太清楚,可以去看一下promise的实现
console.log(4);
resolve(5);
}).then((data) => {
// 这个回调函数叫做callback2,promise属于microtask,所以放到microtask queue中
console.log(data);
});
- Stack Queue: [promise]
- Macrotask Queue: [callback1]
- Microtask Queue: [callback2]
- 打印结果
1 4
Step 4
setTimeout(() => {
// 这个回调函数叫做callback3,setTimeout属于macrotask,所以放到macrotask queue中
console.log(6);
});
- Stack Queue: [setTimeout]
- Macrotask Queue: [callback1, callback3]
- Microtask Queue: [callback2]
- 打印结果
1 4
Step 5
console.log(7);
- Stack Queue: [console]
- Macrotask Queue: [callback1, callback3]
- Microtask Queue: [callback2]
- 打印结果
1 4 7
全局的 Script 代码已执行完毕,进入下一个步骤,从
微队列
中依次取出任务执行,直到微队列
为空
Step 6
console.log(data); // 这里data是Promise的决议值5
- Stack Queue: [callback2]
- Macrotask Queue: [callback1, callback3]
- Microtask Queue: []
- 打印结果
1 4 7 5
微队列
中所有任务执行完毕,开始从宏队列
中执行队首任务
Step 7
console.log(2);
- Stack Queue: [callback1]
- Macrotask Queue: [callback3]
- Microtask Queue: []
- 打印结果
1 4 7 5 2
执行 callback1 时遇到了另一个 Promise,Promise 执行完后在微队列中注册了一个 callback4 回调函数
Step 8
Promise.resolve().then(() => {
// 这个回调函数叫做callback4,promise属于微队列
console.log(3);
});
- Stack Queue: [promise]
- Macrotask : [callback3]
- Microtask Queue: [callback4]
- 打印结果
1 4 7 5 2
执行完一个宏任务后,会再去执行微队列中的所有任务
Step 9
console.log(3);
- Stack Queue: [callback4]
- Macrotask Queue: [callback3]
- Microtask Queue: []
- 打印结果
1 4 7 5 2 3
微队列执行完成后,会再去执行队首的宏任务
Step 10
console.log(6);
- Stack Queue: [callback3]
- Macrotask Queue: []
- Microtask Queue: []
- 打印结果
1 4 7 5 2 3 6
至此所有队列都已清空,最终打印结果为 1 4 7 5 2 3 6
再来一个例子
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3);
});
});
new Promise((resolve, reject) => {
console.log(4);
resolve(5);
}).then((data) => {
console.log(data);
Promise.resolve()
.then(() => {
console.log(6);
})
.then(() => {
console.log(7);
setTimeout(() => {
console.log(8);
}, 0);
});
});
setTimeout(() => {
console.log(9);
});
console.log(10);
答案是:
1 4 10 5 6 7 2 3 9 8
加载过程可视化
参见