Nodejs 事件循环
执行同步代码,将不同的任务添加到相应的队列,所有同步代码执行后会进入事件循环中;
执行宏任务之前,先清空微任务队列,process.nextTick 的优先级大于 promise;
timers 存放计时器的回调函数;
poll 轮询队列 比较特殊,文件 I/O、用户请求 会放入 poll 轮询队列 中:
- 如果 poll 中有回调,依次执行回调,直到清空队列;
- 如果 poll 中没有回调,等待其他队列中出现回调,结束该阶段,进入下一阶段;如果其他队列也没有回调,会持续等待,直到出现回调为止;
check:检查阶段,使用 setImmediate 的回调会直接进入这个队列;
poll 轮询队列
定时器执行,事件循环阻塞在 poll 阶段,等待宏任务队列的任务,直到 fs.readFile 读取文件,做了 1s 的死循环
等待 1s 的过程中,定时器 200ms 结束了,此时只能等待第二轮的事件循环,执行 setTimeout 定时器,输出
setTimeout 1005
const fs = require("fs");
const start = Date.now();
setTimeout(function f1() {
console.log("setTimeout", Date.now() - start); // setTimeout 1005
}, 200);
fs.readFile("./index.js", "utf-8", function f2() {
console.log("readFile");
const start = Date.now();
while (Date.now() - start < 1000) {}
});
setImmediate 效率高于 setTimeout
-
setTimeout 需要遍历所有的 setTimeout 线程,查看哪个 setTimeout 定时器结束了;
let i = 0; console.time('setTimeout'); function test() { i++; if (i < 1000) { setTimeout(test); } else { console.timeEnd('setTimeout'); } } test(); // setTimeout: 1.136s
-
setImmediate 会立即执行
let i = 0; console.time('setImmediate'); function test() { i++; if (i < 1000) { setImmediate(test); } else { console.timeEnd('setImmediate'); } } test(); // setImmediate: 15.165ms
setTimeout 和 setImmediate 执行顺序问题
setTimeout 和 setimmediate 执行顺序是随机的(setimmediate 的效率高于 setTimeout)
当 setTimeout 的参数设置为 0 的时候:
- 如果 cpu 的性能很强,定时器运行完之前就执行到了 check 阶段,此时会先执行 setimmediate,第二轮再执行 setTimeout 的回调函数;
- 如果 cpu 的性能一般,定时器时间到了,先执行 setTimeout 的回调函数,再执行 setimmediate;
setTimeout(() => {
console.log("setTimeout");
}, 0);
setImmediate(() => {
console.log("setImmediate");
});
I/O 事件回调中运行
fs.readFile 处于 poll 队列,会向下继续切换宏任务队列,下一个宏任务队列是 check 队列,则会先执行 setImmediate 函数;
执行第二轮时,再执行 setTimeout 的回调函数,就算定时器时间早就结束了,也要等到第二轮;
所以执行结果永远是 immediate timeout
const fs = require('fs')
fs.readFile('./m1.js', () => {
setTimeout(() => console.log('timeout'), 0)
setImmediate(() => console.log('immdieate'))
})
面试题
看程序写结果 1
输出:3、2、6、4、5、1
setImmediate(() => console.log(1));
process.nextTick(() => {
console.log(2);
process.nextTick(() => console.log(6));
});
console.log(3);
Promise.resolve().then(() => {
console.log(4);
process.nextTick(() => console.log(5));
});
看程序写结果 2
输出:script start、async1 start、async2、promise1、promise2、script end、nextTick、async1 end、promise3、
cpu 的性能会影响到 setTimeout、setImmediate,所以结果会有三种
- setTimeout0、setTimeout3、setImmediate
- setImmediate、setTimeout0、setTimeout3
- setTimeout0、setImmediate、setTimeout3
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
setTimeout(function () {
console.log("setTimeout0");
}, 0);
setTimeout(function () {
console.log("setTimeout3");
}, 3);
setImmediate(() => console.log("setImmediate"));
process.nextTick(() => console.log("nextTick"));
async1();
new Promise(function (resolve) {
console.log("promise1");
resolve();
console.log("promise2");
}).then(function () {
console.log("promise3");
});
console.log("script end");
node👉 https 模块
上一篇