npm install
npx tsc
node lib/xxxx.js
Typescript的团队成员rbuckton主导的TC39提案proposal-explicit-resource-management在2023年的3月的TC39会议上已经进入了Stage 3阶段, 这就意味着它将会成为ECMAScript Next的一员, 所以rbuckton在Typescript 5.2 beta中添加了对应的支持.
Explict Resource Management 提案旨在为 JavaScript 中引入显式的资源管理能力, 如对内存、I/O、文件描述符等资源的分配与显式释放.
显示资源管理意味着, 用户可以主动声明块级作用域内依赖的资源管理, 通过Symbol.dispose这样的命令式或using
这样的声明式, 然后在离开作用域时就能够由运行时自动地释放这些标记的资源.
在此前, Javascript中并不存在标准的资源管理能力, 如以下的函数:
function * g() {
const handle = acquireFileHandle();
try {
...
}
finally {
handle.release(); // 释放资源
}
}
const obj = g();
try {
const r = obj.next();
...
}
finally {
obj.return(); // 显式调用函数 g 中的 finally 代码块
}
在我们执行完生成器函数后, 需要使用obj.return()
来显示地调用g()函数中的finally代码块, 才能确保文件句柄被正确释放.
类似的, 还有NodeJs中对文件描述符的操作(fs.openSync):
export function doSomeWork() {
const path = ".some_temp_file";
const file = fs.openSync(path, "w+");
try {
if (someCondition()) {
return;
}
}
finally {
fs.closeSync(file);
fs.unlinkSync(path);
}
}
这些资源释放的逻辑其实的没有必要的, 正常来说, 这些向操作系统申请的资源应该在代码离开当前作用域时就被释放, 而不需要显示的操作. 因此此提案提出使用using
关键字, 来声明仅在当前作用内使用的资源:
function * g() {
using handle = acquireFileHandle(); // 定义与代码块关联的资源
}
{
using obj = g(); // 显式声明资源
const r = obj.next();
} // 自动调用释放逻辑
{
using handlerSync = openSync();
await using handlerSync = openAsync(); // 同样支持异步的资源声明
} // 自动调用释放逻辑
class Logger implements Disposable {
#flag: number;
constructor(flag: number) {
this.#flag = flag;
}
speak() {
console.log(`[LOG] ${this.#flag}`);
}
[Symbol.dispose](): void {
console.log(`[LOG.DISPOSE] ${this.#flag}`);
}
}
function main() {
using log1 = new Logger(1);
{
using log2 = new Logger(2);
using log3 = new Logger(3);
log1.speak();
}
using log4 = new Logger(4);
log1.speak();
}
/**
* Will Output:
* [LOG] 1
* [LOG.DISPOSE] 3
* [LOG.DISPOSE] 2
* [LOG] 1
* [LOG.DISPOSE] 4
* [LOG.DISPOSE] 1
*/
main();
class Logger implements AsyncDisposable {
#flag: number;
constructor(flag: number) {
this.#flag = flag;
}
speak(): Promise<void> {
return new Promise<void>((resolve, reject) => {
setTimeout(() => {
resolve(console.log(`[LOG] ${this.#flag}`))
}, 20);
});
}
[Symbol.asyncDispose](): Promise<void> {
return new Promise<void>((resolve, reject) => {
setTimeout(() => {
resolve(console.log(`[LOG.DISPOSE] ${this.#flag}`));
}, 20);
});
}
}
async function main() {
await using log1 = new Logger(1);
{
await using log2 = new Logger(2);
await using log3 = new Logger(3);
await log1.speak();
}
await using log4 = new Logger(4);
await log1.speak();
}
/**
* Will Output:
[LOG] 1
[LOG.DISPOSE] 3
[LOG.DISPOSE] 2
[LOG] 1
[LOG.DISPOSE] 4
[LOG.DISPOSE] 1
*/
main();
function disposableStackDemo() {
const result: string[] = [];
using stack = new DisposableStack();
result.push("start");
stack.use(((): Disposable => {
return {
[Symbol.dispose]() {
result.push("s1");
}
}
})());
stack.use(((): Disposable => {
return {
[Symbol.dispose]() {
result.push("s2");
}
}
})());
result.push("end");
return result;
}
function main() {
const result = disposableStackDemo();
console.log(result);
}
/**
* Will Output: ["start", "end", "s2", "s1"]
*/
main();
- 在项目中添加这个依赖
npm install core-js
- tsconfig.json的内容
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "ESNext"],
// ....
}
}
- 将下面的代码块添加到ts文件中
import "core-js/modules/esnext.symbol.dispose.js";
import "core-js/modules/esnext.symbol.async-dispose.js";
import "core-js/modules/esnext.disposable-stack.constructor.js";