配了多年资源管理,今天才发现 Node.js 24 的内置 API 早就实现了 Symbol.dispose——但你可能一次都没用过

你以为 try-finally 已经够用了?今天 Symbol.dispose 把这件事彻底原生化了。

Node.js 24 带来了 V8 13.6 引擎,也把显式资源管理(Explicit Resource Management)彻底稳稳地接进了运行时层。Chrome 134 默认开启,Node.js 24 默认开启——这意味着你不只可以在代码里用 using 关键字,你还可以在 Node.js 自己的内置 API 里看到 Symbol.dispose 已经悄悄被实现了。

这是什么意思?

过去你写文件读取要这样:

import fs from 'node:fs';

function process() {
  let file;
  try {
    file = fs.openSync('data.txt', 'r');
    // 读取文件...
  } finally {
    if (file) fs.closeSync(file); // 手动关
  }
}

现在 Node.js 24 的 fs 文件句柄、http Agent、stream 都实现了 Symbol.dispose。你只需要把文件句柄放进 using 作用域,出了作用域浏览器自动调用 [Symbol.dispose]() 关闭它:

import fs from 'node:fs';

function process() {
  using file = fs.openSync('data.txt', 'r');
  // 读取文件...
  // 函数结束,file 自动关闭
}

但这只是语法糖——真正的变化在于 Node.js 自己的 API 在帮你做这件事。

Node.js 24 的 node:test 测试运行器内部已经全面使用 Symbol.dispose 来管理测试资源。fs 打开的文件句柄实现了 dispose,HTTP 请求的 Agent 也实现了 dispose,stream 在结束后自动调用 Symbol.dispose 做清理。

这就是为什么「你不需要为 Node.js 内置 API 写 finally」了——因为它们自己已经实现了这个协议。

async 的场景也一样。Node.js 24 的 readline 接口实现了 Symbol.asyncDispose,用 await using 可以这样写:

import readline from 'node:readline/promises';

{
  await using rl = readline.createInterface({
    input: process.stdin,
  });
  const answer = await rl.question('> ');
  console.log(answer);
}
// 函数结束,readline 自动关闭,不需要 rl.close()

setInterval 也一样——放进 using 作用域,作用域结束时 interval 自动清除,不会内存泄漏:

{
  using timer = setInterval(() => {
    console.log('tick');
  }, 1000);
  // ...做点事情
} // timer 自动清除

生产环境怎么用?

三个落地路径:

1. 写自己的资源类

只要你的类实现了 [Symbol.dispose]() 方法,就能参与 using 作用域:

class Logger {
  #stream;
  constructor(path) {
    this.#stream = fs.createWriteStream(path);
  }
  log(msg) { this.#stream.write(msg + 'n'); }
  [Symbol.dispose]() { this.#stream.end(); }
}

// 使用
{
  using logger = new Logger('/tmp/app.log');
  logger.log('hello');
} // 自动关闭

2. 配合 DisposableStack 做资源组合

import { DisposableStack } from 'node:async_hooks';

{
  using stack = new DisposableStack();
  const file = stack.use(fs.openSync('a.txt', 'r'));
  const conn = stack.use(db.connect());
  // 所有资源在作用域结束时按 LIFO 顺序自动释放
}

3. 现有代码不用改,渐进迁移

Node.js 24 对旧代码完全兼容。你的现有 try-finally 代码继续工作,新增的 API 调用自动享受 Symbol.dispose 清理。TypeScript 5.2+ 已经支持 usingawait using 语法,可以直接在 VS Code 里跑。

总结

Symbol.dispose 不是一个新语法——它是让 JavaScript 对象声明「我实现了销毁逻辑」的接口协议。Node.js 24 让内置 API 全部实现了这个协议,你不需要再手动关闭 fs 文件句柄、readline 接口、http Agent——它们自己管自己。

下一步:打开 Node.js 24 终端,跑一下 node --version,确认是 24 以上,然后把你项目里最长的那个 finally 块替换成 using,看看能省多少行。

评论区

0 条评论

登录后可评论。