写过八年 JavaScript,每次资源清理都要靠 try/finally——今天 using 关键字把这件事彻底变了
写过文件读取,你一定见过这套:
let handle;
try {
handle = await openFile('data.txt');
// 读文件
} finally {
await handle?.close(); // 清理
}
或者更保守一点,连初始化的那行也包进 try:
let handle;
try {
handle = await openFile('data.txt');
const data = await handle.read();
// …
} finally {
await handle?.close();
}
每次写这种代码我都要提醒自己:别漏了 finally,别漏了 await close,别在 finally 里再抛异常——三层考虑全靠习惯约束,没有任何语言层面的保障。
2026 年 8 月 13 日,这套写法正式过时了。
Safari 把最后一块拼图补上了
苹果的 WebKit 团队在 Safari Technology Preview 250 里完整支持了 TC39 的 Explicit Resource Management 提案。这个提案在 2025 年推进到 Stage 4,已经是 ECMAScript 2026 的正式组成部分。V8(Chrome / Node.js)和 SpiderMonkey(Firefox)早就实现了它,唯独 Safari 一直缺着。
STP 250 出来的那一刻,三大引擎第一次在 using 关键字上会师。这意味着它不再是 TypeScript 的转译目标,而是真正的原生运行时能力——不用 polyfill,不用编译,直接在浏览器里跑。
用 using 改写之后
// 之前:try/finally,三件事全靠人
let handle;
try {
handle = await openFile('data.txt');
} finally {
await handle?.close();
}
// 之后:using,退出作用域自动调用 [Symbol.dispose]()
{
using handle = await openFile('data.txt');
// 读文件
} // ← 作用域结束,浏览器自动调用 handle[Symbol.dispose]()
关键点在于:只要对象实现了 [Symbol.dispose](),离开 using 声明所在的作用域时,引擎就会自动调用它。不管是正常 return、抛异常、还是代码走到作用域底部,清理逻辑都会触发。
多资源按 LIFO 顺序自动清理,最后打开的先关:
{
using file = await openFile('data.txt');
using conn = await db.connect();
// …业务逻辑
} // 先关 conn,再关 file
这和你手动写 finally 的顺序一致,不用再数栈的进出顺序。
抛异常的时候怎么办
之前 finally 里如果清理代码本身也抛异常,原来的错误就被吃掉了。新的 SuppressedError 把两个错都包在一起:
{
using handle = await getHandle();
throw new Error('业务异常');
} // handle.close() 抛错了 → 变成 SuppressedError,包含原始错误和清理错误
不是无声消失,也不是只留清理错——两个都保留。
异步资源用 await using
{
await using conn = await db.connect();
// …
} // 自动调用 conn[Symbol.asyncDispose]()
await using 配合 [Symbol.asyncDispose](),异步清理同样有保障。
谁在用这个
V8 团队在官方文档里明确推荐用它管理文件句柄、网络连接、流读取器、锁。Node.js 侧只要实现了 [Symbol.dispose] 的对象都可以用,不挑运行环境。
TypeScript 从 5.2 开始支持 using,Transpiler 模式一直是 TS 开发者管理资源的主流做法。现在 Safari 补上了,三大引擎都通,线上代码终于可以卸掉编译这一步直接跑。
现在能用在生产环境吗
主流环境覆盖情况:
- Chrome / Edge:稳定支持(V8 内核)
- Firefox:稳定支持(SpiderMonkey)
- Safari:Technology Preview 250(2026-08-13),稳定版预计 2026 年内跟上
- Node.js 22+:稳定支持
Safari 这块是唯一的预览状态。如果你的用户里 Safari/iOS 占比不高,现在就可以把 TypeScript 里的 using 编译降级关掉,直接写原生语法。如果占比高,可以用 Symbol.dispose polyfill + TypeScript 类型做渐进迁移,语法不变。
怎么迁移手上的代码
最直接的做法:把现有 try/finally 里的手动清理替换成 using/await using,把 [Symbol.dispose] 或 [Symbol.asyncDispose] 实现到你的资源包装类里。Node.js 的文件句柄、数据库连接池、缓存客户端基本都已经是 [Symbol.asyncDispose] 的实现方,直接 await using 就行。
三年 TS 写下来,using 一直是编译输出的代码。现在它回到 JavaScript 自己的语法里,try/finally 那套写法可以正式归档了。
相关链接:
- TC39 Explicit Resource Management 提案:https://github.com/tc39/proposal-explicit-resource-management
- V8 官方文档:https://v8.dev/features/explicit-resource-management
- Safari Technology Preview 250 发布说明:https://webkit.org/blog/18191/release-notes-for-safari-technology-preview-250/
评论区
登录后可评论。