«

nodeJs事件循环运行代码怎么写

时间:2024-7-3 13:29     作者:韩俊     分类: Javascript


这篇文章主要讲解了“nodeJs事件循环运行代码怎么写”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“nodeJs事件循环运行代码怎么写”吧!

    Nodejs运行时

    JS语言是同步,阻塞,单线程的,但是nodejs不是。Nodejs由三个主要组件:

      外部依赖例如 v8,libuv,crypto

      提供文件和网络服务的c++模块

      基于c++模块上层封装的JS库

    nodejs的异步特性主要由libuv提供。libuv是跨平台的使用c语言写的库,它主要提供对异步操作的支持。

    node运行时代码运行

    当我们在Nodejs中执行JS代码时,是由v8引擎处理代码执行,v8包括一块内存区域(堆)和调用栈。当定义函数,变量时,从堆中分配内存,当执行代码时将函数入栈,函数返回时出栈。

    当执行异步操作时,libuv将接管该任务,然后使用操作系统的异步机制运行任务。如果缺乏系统级的异步机制,就使用线程池运行任务,保证主线程不被阻塞。

    Event Loop

    事件循环是一个nodejs应用运行后一直存在的循环。存在着六个不同的队列,每个都存储着不同的回调。

      Timer queue(定时器队列),最小堆,由setTimeout, setInterval创建

      IO队列:文件、网络操作

      check队列,任务由setImmediate产生,node专有

      close队列, 与异步任务的close事件相关

      nextTick队列

      promise队列

    除了两个微任务队列,其他队列都是libuv自带的

    如何工作?

    同步代码优于异步代码,事件循环是call stack为空后开始。事件循环遵循的优先级规则:

      微任务队列有任务,先处理完。nextTick先于promise

      定时器任务执行

      IO队列

      check队列

      close队列

    需要注意的是在定时器队列,IO队列,check队列,close队列执行一个任务后都会检查并运行微任务队列。

    实验

    实验1

    // index.js
    console.log("console.log 1");
    process.nextTick(() => console.log("this is process.nextTick 1"));
    console.log("console.log 2");

    输出

    console.log 1
    console.log 2
    this is process.nextTick 1

    结论: 同步先于异步

    实验2

    // index.js
    Promise.resolve().then(() => console.log("this is Promise.resolve 1"));
    process.nextTick(() => console.log("this is process.nextTick 1"));

    输出

    this is process.nextTick 1
    this is Promise.resolve 1

    结论: nextTick先于promise

    实验3

    // index.js
    process.nextTick(() => console.log("this is process.nextTick 1"));
    process.nextTick(() => {
      console.log("this is process.nextTick 2");
      process.nextTick(() =>
        console.log("this is the inner next tick inside next tick")
      );
    });
    process.nextTick(() => console.log("this is process.nextTick 3"));
    Promise.resolve().then(() => console.log("this is Promise.resolve 1"));
    Promise.resolve().then(() => {
      console.log("this is Promise.resolve 2");
      process.nextTick(() =>
        console.log("this is the inner next tick inside Promise then block")
      );
    });
    Promise.resolve().then(() => console.log("this is Promise.resolve 3"));

    实验3

    // index.js
    process.nextTick(() => console.log("this is process.nextTick 1"));
    process.nextTick(() => {
      console.log("this is process.nextTick 2");
      process.nextTick(() =>
        console.log("this is the inner next tick inside next tick")
      );
    });
    process.nextTick(() => console.log("this is process.nextTick 3"));
    Promise.resolve().then(() => console.log("this is Promise.resolve 1"));
    Promise.resolve().then(() => {
      console.log("this is Promise.resolve 2");
      process.nextTick(() =>
        console.log("this is the inner next tick inside Promise then block")
      );
    });
    Promise.resolve().then(() => console.log("this is Promise.resolve 3"));

    输出

    this is process.nextTick 1
    this is process.nextTick 2
    this is process.nextTick 3
    this is the inner next tick inside next tick
    this is Promise.resolve 1
    this is Promise.resolve 2
    this is Promise.resolve 3
    this is the inner next tick inside Promise then block

    解析:

    nextTick内部增加的nextTick任务还是先于promise,因为nexttick队列清完后才会执行promise队列的任务。

    promise里增加的nextTick任务晚于其他的promise,因为此时是在执行promise阶段,需要清空promise才会检查nextTick队列。

    实验4

    // index.js
    setTimeout(() => console.log("this is setTimeout 1"), 0);
    setTimeout(() => {
      console.log("this is setTimeout 2");
      process.nextTick(() =>
        console.log("this is inner nextTick inside setTimeout")
      );
    }, 0);
    setTimeout(() => console.log("this is setTimeout 3"), 0);
    process.nextTick(() => console.log("this is process.nextTick 1"));
    process.nextTick(() => {
      console.log("this is process.nextTick 2");
      process.nextTick(() =>
        console.log("this is the inner next tick inside next tick")
      );
    });
    process.nextTick(() => console.log("this is process.nextTick 3"));
    Promise.resolve().then(() => console.log("this is Promise.resolve 1"));
    Promise.resolve().then(() => {
      console.log("this is Promise.resolve 2");
      process.nextTick(() =>
        console.log("this is the inner next tick inside Promise then block")
      );
    });
    Promise.resolve().then(() => console.log("this is Promise.resolve 3"));

    输出

    this is process.nextTick 1
    this is process.nextTick 2
    this is process.nextTick 3
    his is the inner next tick inside next tick
    this is Promise.resolve 1
    this is Promise.resolve 2
    this is Promise.resolve 3
    this is the inner next tick inside Promise then block
    this is setTimeout 1
    this is setTimeout 2
    this is inner nextTick inside setTimeout
    this is setTimeout 3

    结论:

    nextTick先于promise;微任务先于setTimeout;每个Timer任务后会检查执行微任务。

    实验6

    // index.js
    setTimeout(() => console.log("this is setTimeout 1"), 1000);
    setTimeout(() => console.log("this is setTimeout 2"), 500);
    setTimeout(() => console.log("this is setTimeout 3"), 0);

    输出

    this is setTimeout 3
    this is setTimeout 2
    this is setTimeout 1

    结论: Timer队列是按时间排序的

    实验7

    // index.js
    const fs = require("fs");
    fs.readFile(__filename, () => {
      console.log("this is readFile 1");
    });
    process.nextTick(() => console.log("this is process.nextTick 1"));
    Promise.resolve().then(() => console.log("this is Promise.resolve 1"));

    输出

    this is process.nextTick 1
    this is Promise.resolve 1

    结论:微任务先于io任务

    实验8

    // index.js
    const fs = require("fs");
    setTimeout(() => console.log("this is setTimeout 1"), 0);
    fs.readFile(__filename, () => {
      console.log("this is readFile 1");
    });

    输出

    不确定

    解析:setTimeout 0通常内部会取1ms,也就是1ms后执行Timer任务,而cpu进入事件循环的时机不定,所以有可能进入事件循环时已经过了1ms,那么先执行timer任务,也可能进入时定时任务没到时间,会先执行IO任务。

    实验9

    // index.js
    const fs = require("fs");
    fs.readFile(__filename, () => {
      console.log("this is readFile 1");
    });
    process.nextTick(() => console.log("this is process.nextTick 1"));
    Promise.resolve().then(() => console.log("this is Promise.resolve 1"));
    setTimeout(() => console.log("this is setTimeout 1"), 0);
    for (let i = 0; i < 2000000000; i++) {}

    输出

    this is process.nextTick 1
    this is Promise.resolve 1
    this is setTimeout 1
    this is readFile 1

    解析:

    代码最后加了循环保证进入事件循环时定时器任务已经到期,所以先执行Timer任务

    实验10

    // index.js
    const fs = require("fs");
    fs.readFile(__filename, () => {
      console.log("this is readFile 1");
    });
    process.nextTick(() => console.log("this is process.nextTick 1"));
    Promise.resolve().then(() => console.log("this is Promise.resolve 1"));
    setTimeout(() => console.log("this is setTimeout 1"), 0);
    setImmediate(() => console.log("this is setImmediate 1"));
    for (let i = 0; i < 2000000000; i++) {}

    输出

    this is process.nextTick 1
    this is Promise.resolve 1
    this is setTimeout 1
    this is setImmediate 1
    this is readFile 1

    解析: 按理说IO任务先于check任务,但是第一次事件循环时IO任务的callback并不在队列里。在两个队列之间会通过IO polling的方式去查看io任务是否完成,完成了就将callback加到队列里,然后下一轮循环时会调用

    标签: javascript

    热门推荐