Nodejs 事件循环

  1. 执行同步代码,将不同的任务添加到相应的队列,所有同步代码执行后会进入事件循环中;

  2. 执行宏任务之前,先清空微任务队列process.nextTick 的优先级大于 promise

  3. timers 存放计时器的回调函数;

  4. poll 轮询队列 比较特殊,文件 I/O用户请求 会放入 poll 轮询队列 中:

    • 如果 poll 中有回调,依次执行回调,直到清空队列;
    • 如果 poll 中没有回调,等待其他队列中出现回调,结束该阶段,进入下一阶段;如果其他队列也没有回调,会持续等待,直到出现回调为止;
  5. check:检查阶段,使用 setImmediate 的回调会直接进入这个队列;

poll 轮询队列

  1. 定时器执行,事件循环阻塞在 poll 阶段,等待宏任务队列的任务,直到 fs.readFile 读取文件,做了 1s 的死循环

  2. 等待 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

  1. 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
    
  2. 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 执行顺序问题

  1. setTimeoutsetimmediate 执行顺序是随机的(setimmediate 的效率高于 setTimeout

  2. setTimeout 的参数设置为 0 的时候:

    • 如果 cpu 的性能很强,定时器运行完之前就执行到了 check 阶段,此时会先执行 setimmediate,第二轮再执行 setTimeout 的回调函数;
    • 如果 cpu 的性能一般,定时器时间到了,先执行 setTimeout 的回调函数,再执行 setimmediate
setTimeout(() => {
  console.log("setTimeout");
}, 0);

setImmediate(() => {
  console.log("setImmediate");
});

I/O 事件回调中运行

  1. fs.readFile 处于 poll 队列,会向下继续切换宏任务队列,下一个宏任务队列是 check 队列,则会先执行 setImmediate 函数;

  2. 执行第二轮时,再执行 setTimeout 的回调函数,就算定时器时间早就结束了,也要等到第二轮;

  3. 所以执行结果永远是 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

  1. 输出:script startasync1 startasync2promise1promise2script endnextTickasync1 endpromise3

  2. cpu 的性能会影响到 setTimeout、setImmediate,所以结果会有三种

    • setTimeout0setTimeout3setImmediate
    • setImmediatesetTimeout0setTimeout3
    • setTimeout0setImmediatesetTimeout3
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");
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. Nodejs 事件循环
  2. 2. poll 轮询队列
  3. 3. setImmediate 效率高于 setTimeout
  4. 4. setTimeout 和 setImmediate 执行顺序问题
  5. 5. I/O 事件回调中运行
  6. 6. 面试题
    1. 6.1. 看程序写结果 1
    2. 6.2. 看程序写结果 2