CcbeanBlog CcbeanBlog
首页
  • 前端文章

    • JavaScript
    • HTML+CSS
    • Vue
    • React
  • 系列笔记

    • React使用学习
    • Vue2源码探究
  • Node文章

    • 基础
    • 问题
    • 框架
  • 系列笔记

    • 数据结构与算法
  • 构建工具文章

    • webpack
  • 系列笔记

    • Webpack5使用学习
  • MySQL
  • Linux
  • 网络
  • 小技巧
  • 杂记
  • 系列笔记

    • Protobuf Buffers
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Ccbean

靡不有初,鲜克有终
首页
  • 前端文章

    • JavaScript
    • HTML+CSS
    • Vue
    • React
  • 系列笔记

    • React使用学习
    • Vue2源码探究
  • Node文章

    • 基础
    • 问题
    • 框架
  • 系列笔记

    • 数据结构与算法
  • 构建工具文章

    • webpack
  • 系列笔记

    • Webpack5使用学习
  • MySQL
  • Linux
  • 网络
  • 小技巧
  • 杂记
  • 系列笔记

    • Protobuf Buffers
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础

    • NodeJS代码规范
    • Node的简介与模块机制
    • Node实现图片上传
    • 事件循环
      • 事件循环
        • 阶段概述
        • Timers阶段
        • Pending阶段
        • Idle、Prepare阶段
        • Poll阶段
        • Check阶段
        • Close阶段
    • 异步编程
  • 问题

  • 框架

  • 系列笔记

  • Node
  • 基础
ccbean
2021-08-27
目录

事件循环

Node的特点:

  • 单线程
  • 异步I/O
  • 事件和回调

下面的问题:输出顺序分别是什么?

问题1

setTimeout(() => {
  console.log('timeout');
}, 0);

setImmediate(() => {
  console.log('immediate');
});

// 输出结果有两种可能
// timeout
// immediate
// 或
// immediate
// timeout

问题2

const fs = require('fs');

Promise.resolve().then(() => {
  console.log('123');
})

fs.readdir('./test', () => {
  setTimeout(() => {
    console.log('timeout');
  }, 0);

  Promise.resolve().then(() => {
    console.log('456');
  })

  setImmediate(() => {
    console.log('immediate');
  });
});
// 输出顺序
// 123
// 456
// immediate
// timeout

问题3

const fs = require('fs');

(function mainline() {
  process.nextTick(() => {
    console.log('A');
  });
  console.log('B');
  setTimeout(() => {
    console.log('C');
  }, 2000);
  setImmediate(() => {
    console.log('D');
  });
  setTimeout(() => {
    console.log('E');
  }, 0);
  fs.readdir('./test', 'utf8', (err, files) => {
    console.log('F');
    setTimeout(() => {
      console.log('G');
    }, 0);
    setImmediate(() => {
      console.log('H');
    });
    process.nextTick(() => {
      console.log('I');
    });
  });
  
  console.log('J');
})();

// B J A E D F I H G C
// 或
// B J A E F I D H G C

执行顺序为什么会是这样的结果,这和事件循环有关。

# 事件循环

启动Node,我们所编写的代码执行位置:

  • 主线
  • 事件循环

什么是事件循环,事件循环其实是Node.js处理非阻塞I/O操作的机制

Node 应用程序生命周期

事件循环每个周期就是一个Tick,每个阶段有自己的FIFO队列

非阻塞I/O:

  • 异步I/O:Node提供的内部异步API;
  • 非异步I/O:
    • setTimeout()
    • setInterval()
    • setImmediate()
    • process.nextTick()

# 阶段概述

Timer阶段:执行setTimeout() 和 setInterval()调度的回调函数

Pending阶段:延迟到下一个循环迭代的I/O操作;如tcp报错,等待错误

idle, prepare阶段:仅系统内部使用

Poll(轮询)阶段:检索新I/O;执行与I/O相关回调

check阶段:执行setImmediate()调度的回调函数

close阶段:一些关闭的回调函数,如socket.on('close', ...)

# Timers阶段

定时器会的回调会尽可能早地被调用。

轮询阶段控制定时器何时执行。

# Pending阶段

延迟到下一个循环迭代的I/O操作。

此阶段对某些系统操作(如 TCP 错误类型)执行回调。例如,如果 TCP 套接字在尝试连接时接收到 ECONNREFUSED,则某些 *nix 的系统希望等待报告错误。这将被排队以在 挂起的回调 阶段执行。

# Idle、Prepare阶段

仅系统内部使用

# Poll阶段

重要功能:

  • 计算应该阻塞和轮询 I/O 的时间
  • 处理轮询队列里面的事件

当进入轮询阶段,分两种情况:

  1. 如果有被调度的计时器,那么会先执行计时器,然后再执行需要调度的异步I/O
  2. 如果没有被调度的计时器,那么还会发生两种情况:
    1. 如果脚本被 setImmediate() 调度,那么会结束轮询阶段,进入check阶段,并执行被setImmediate()调度的脚本。
    2. 如果脚本未被setImmediate()调度,则事件循环将等待回调被添加到队列中,然后立即执行。

Timer > 异步I/O > Check

# Check阶段

允许在轮询阶段完成后立即执行回调。

即一旦结束当前轮询就会执行。

# Close阶段

如果套接字或处理函数突然关闭(例如 socket.destroy()),则'close' 事件将在这个阶段发出。否则它将通过 process.nextTick() 发出。

  1. https://nodejs.org/zh-cn/docs/guides/event-loop-timers-and-nexttick/
  2. https://developer.ibm.com/tutorials/learn-nodejs-the-event-loop/
编辑 (opens new window)
上次更新: 2021/11/10, 12:11:50
Node实现图片上传
异步编程

← Node实现图片上传 异步编程→

最近更新
01
阅读精通正则表达式总结
09-29
02
项目搭建规范的配置
07-15
03
Vite的使用
07-03
更多文章>
Theme by Vdoing | Copyright © 2018-2023 Ccbeango
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式