# 事件循环
事件循环是一个依赖执行环境实现的语言执行特性,在 node 的 libuv 和 浏览器环境内的执行逻辑是不一样的
- Node端,microtask 在事件循环的各个阶段之间执行
- 浏览器端,microtask 在事件循环的 macrotask 执行完之后执行
nodejs V11.0 以上 这两者之间的顺序就相同了
# 浏览器环境
主进程的代码也属于macroTask,宏任务执行完后,UI重新渲染
# macrotasks
- script(整体代码), setImmediate, setTimeout, setInterval, I/O, UI rendering, requestAnimationFrame
# microtasks
- process.nextTick, Promises, Object.observe, MutationObserver
# 例子
setImmediate(function () {
console.log(1);
}, 0);
setTimeout(function () {
console.log(2);
}, 0);
new Promise(function (resolve) {
console.log(3);
resolve();
console.log(4);
}).then(function () {
console.log(5);
});
console.log(6);
process.nextTick(function () {
console.log(7);
});
console.log(8);
requestAnimationFrame(() => console.log(9))
// 3 4 6 8 7 5 1 2 9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 总结
在每一次事件循环中,macrotask 只会提取一个执行,而 microtask 会一直提取,直到 microtasks 队列清空
注:一般情况下,macrotask queues 我们会直接称为 task queues,只有 microtask queues 才会特别指明
# Node 环境
之前有做过一期详细的分享,大家可以看看这篇文章 https://github.com/PeterChen1997/MyBlog/issues/6
简单来说,node 内的事件循环执行逻辑如下:
- 处理到期timers的回调
- 处理 I/O 回调
- idle/prepare(内部函数,可以不关注)
- 等等有没有处理完毕的事件
- check (内部函数,此时执行setImmediate)
- 关闭handle并调用对应的回调函数
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 小结
- setTimeout 和 setImmediate 0 在 node 内的执行顺序是不一定的,但是在 io 回调内,一定是 immediately 先执行
← 事件委托详解 (Delegate) 作用域 →