🍂落页
登 录

《Node.js 实战》笔记

  • Node.js
  • 协议
  • 扩展
  • 可观测性
🍂落页
TALAXY
Node.js
🍂落页
TALAXY

Node.js

Node.js
  • 事件循环注意点

JS 通过事件循环来处理并发。

将来使用新执行栈调用的函数称为异步运行;反之,在同一执行栈执行的函数称为同步运行。


Node.js 的层:

  • 应用程序代码 - JS
  • Node.js 核心 API - JS
  • Node.js binding - C++
  • V8(JS 引擎)、libuv、OpenSSL... - C++
  • 操作系统

I/O 操作通常需要花费较长时间。Node 本身是多线程的,其底层使用的 libuv 可以处理操作系统抽象及管理 I/O 操作,并维护一个线程池。当 JS 正常运行时,Node.js 内部可能正忙于读取文件内容。

当没有异步任务来让进程保持活动状态时,Node.js 就会终止退出。Node 也提供了一些方式使异步任务不再保持进程为活动状态,比如:

const timer = setTimeout(() => {}, 1000);
timer.unref();  // 仍然会执行回调,但不会让进程保持活动状态。

循环队列有以下几个阶段:

  1. 轮询 - 执行 I/O 相关的回调。主代码开始时会在这个阶段运行。
  2. 检查 - 执行 setImmerdiate() 触发的回调。
  3. 关闭 - 执行通过 EventEmitter 的 close 事件触发的回调。
  4. 定时器 - 执行 setTimeout() 或 setInterval() 触发的回调。
  5. 挂起 - 运行特殊的系统事件。

每个阶段都会维护一个待执行的回调队列,在 JS 代码运行时,就会往不同队列里添加回调。


还有两个微任务队列,会在每个阶段工作前执行:

  • process.nextTick()
  • Promise

其中 process.nextTick() 会优于 Promise 执行。

事件循环注意点

在单个执行栈里不要运行太多代码,这样会导致事件循环暂停并阻止触发其他回调。可以将大型任务分批处理,在每个批次结束时通过 setImmediate() 来执行下一个批次。

不要用 process.nextTick() 做类似下面代码的事情,会导致微任务队列永远不会清空,应用程序会永远困在同一阶段里:

const forever = () => process.nextTick(forever);
forever();

setInterval(() => console.log('hello'), 10); // 永远不会执行

在设计一个函数或者 API 时,最好保证其要么是同步或者异步执行,比如:

function foo(count, callback) {
    if (count <= 0) {
        // 该回调是同步调用的
        return callback(new TypeError('count > 0'));
    }
    myAsyncOperation(count, callback);
}

为了确保回调是在一个新栈中执行,可以用 process.nextTick() 或者 setImmediate() 包裹同步代码。

TALAXY 落于 2024年4月6日 。