我眼中的event loop
科普一波
- js执行是单线程的,执行顺序是 macro-task -> micro-task -> macro-task -> ... 交替执行
- macro-task,以下简称macro,包含:js整体代码、setTimeout、setInterval、setImmediate、IO、UI rendering
- micro-task,以下简称micro,包含:process.nextTick、Promises(这里指浏览器实现的Promise)、Object.observe(已废弃)、MutationObserver
上代码(执行环境为Node 8.9.4)
setImmediate(function () {
console.log(14);
});
setTimeout(function () {
console.log(13)
});
process.nextTick(function () {
console.log(4);
});
new Promise(function (resolve) {
console.log(1);
for (let i = 0; i < 10000; i++) {
i === 9999 && resolve()
}
console.log(2);
process.nextTick(function () {
console.log(5);
});
}).then(function () {
console.log(6);
process.nextTick(function () {
console.log(10);
});
new Promise(function (resolve) {
console.log(7);
resolve();
console.log(8);
process.nextTick(function () {
console.log(11);
});
}).then(function () {
console.log(9);
process.nextTick(function () {
console.log(12);
});
})
});
console.log(3);
执行结果为 1 2 3 ... 14
开始分析
-
第一轮
执行macro,也就是js代码,输出1 2 3;
此时micro中注册了[4 5 <包含6~12的Promise.then代码块>],macro中注册了[13 14];
macro已经清空,之后执行micro; -
第二轮
执行micro,输出4 5,可以看出process.nextTick优先级高于Promise.then;
接着执行micro,执行<包含6~12的Promise.then代码块>,首先输出6 7 8,此时micro再次注册[10 11 <包含9 12的Promise.then代码块>];
接着执行micro,按照上面得出的结论,讲道理输出顺序为 10 11 9 12,实际输出为 9 10 11 12,为什么会这样呢? -
下面开始分析,纯属猜测!!!
本人能想到的就是,micro不止一个,process.nextTick拥有单独的micro-task队列;
回头看<包含6~12的Promise.then代码块>,首先输出 6 7 8,没毛病;
此时nextTick独有的micro注册了[10 11],当前micro还注册了[<包含9 12的Promise.then代码块>];
由于当前micro还没执行完,接着执行<包含9 12的Promise.then代码块>,此时输出 9,并且向nextTick独有的micro注册了12,
此时nextTick独有的micro包含 [10 11 12],这时,当前micro已经清空,接着执行nextTick的micro,输出 10 11 12;
这样就与实际情况相符了~,不过,以上纯属个人猜测!!! -
最后一轮
执行macro,输出 13 14,可以看出setTimeout优先级高于setImmediate;