Nodejs事件循环机制(二)

2021-07-27  本文已影响0人  Peter杰

浏览器中的Eventloop是根据html5定义的规范来实现的,不同的浏览器会有不同的实现,而node中是由libuv实现的。
Node.js® 是一个基于Chrome 的 V8 JavaScript 引擎构建的 JavaScript 运行时。
nodejs架构图

这个图是整个 Node.js 的运行原理,从左到右,从上到下,Node.js 被分为了四层,分别是 应用层V8引擎层Node API层LIBUV层

nodejs具有事件驱动和非阻塞I/O的特点。
事件驱动是指nodejs把每一个任务当成事件来处理。

非阻塞I/O是指nodejs遇到I/O任务时,会从线程池调度单独的线程处理I/O操作,不会阻塞主线程。

宏任务与微任务

宏任务(macrotask):setTimeout、setInterval、IO事件、setImmediate、close事件
微任务(microtask):Promise的then回调、queueMicrotask、process.nextTick

微任务队列:
next tick queue:process.nextTick
other queue:Promise的then回调、queueMicrotask
宏任务队列:
timer queue:setTimeout、setInterval
poll queue:IO事件
cheak queue:setImmediate
close queue:close事件

执行顺序:
微任务优先于宏任务
next tick queue>other queue>
timer queue>timer queue>poll queue>cheak queue>close queue

事件循环原理

当Node.js启动时会初始化event loop, 每一个event loop都会包含按如下顺序六个循环阶段:

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘

  1. timers 阶段: 这个阶段执行 setTimeout(callback) 和 setInterval(callback) 预定的 callback;
  2. I/O callbacks 阶段: 此阶段执行某些系统操作的回调,例如TCP错误的类型。 例如,如果TCP套接字在尝试连接时收到 ECONNREFUSED,则某些* nix系统希望等待报告错误。 这将操作将等待在==I/O回调阶段==执行;
  3. idle, prepare 阶段: 仅node内部使用;
  4. poll 阶段: 获取新的I/O事件, 例如操作读取文件等等,适当的条件下node将阻塞在这里;
  5. check 阶段: 执行 setImmediate() 设定的callbacks;
  6. close callbacks 阶段: 比如 socket.on(‘close’, callback) 的callback会在这个阶

libuv

核心代码uv_run:

nt uv_run(uv_loop_t* loop, uv_run_mode mode) {
  int timeout;
  int r;
  int ran_pending;

  r = uv__loop_alive(loop);
  if (!r) uv__update_time(loop);

  while (r != 0 && loop->stop_flag == 0) {
    // 更新时间变量,这个变量在uv__run_timers中会用到
    uv__update_time(loop);
    // timers阶段
    uv__run_timers(loop);
    // 从libuv的文档中可知,这个其实就是I/O
    // callback阶段,ran_pending指示队列是否为空
    ran_pending = uv__run_pending(loop);
    // idle阶段,仅node内部使用;
    uv__run_idle(loop);
    // prepare阶段,仅node内部使用;
    uv__run_prepare(loop);

    timeout = 0;
    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
      timeout = uv_backend_timeout(loop);
    // poll阶段
    uv__io_poll(loop, timeout);
    uv__metrics_update_idle_time(loop);
    // check阶段
    uv__run_check(loop);
    // close阶段
    uv__run_closing_handles(loop);

    // 如果mode==UV_RUN_ONCE(意味着流程继续向前)时,在所有阶段结束后还会检查一次timers

    if (mode == UV_RUN_ONCE) {
      uv__update_time(loop);
      uv__run_timers(loop);
    }
    r = uv__loop_alive(loop);
    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) break;
  }
  if (loop->stop_flag != 0) loop->stop_flag = 0;

  return r;
}
上一篇 下一篇

猜你喜欢

热点阅读