js中的using声明

一、using 声明简介

usingECMAScript 2023(ES14) 引入的一项新语法,用于自动管理资源的生命周期

它的主要目标是简化"资源使用完后自动释放"的场景,例如文件句柄、数据库连接、锁等。 相关提案可见:github.com/tc39/propos...

📜 语法结构:

csharp 复制代码
using 变量名 = 表达式;
await using 变量名 = 异步表达式;

using 声明的变量必须是一个实现了 Symbol.disposeSymbol.asyncDispose 方法的对象。

当作用域退出(无论是正常返回还是异常)时,JS 引擎会自动调用该方法释放资源。


二、基本使用示例

同步版本

javascript 复制代码
class File {
  constructor(name) {
    this.name = name;
    console.log(`打开文件: ${name}`);
  }
  [Symbol.dispose]() {
    console.log(`关闭文件: ${this.name}`);
  }
}

function main() {
  using f = new File("data.txt");
  console.log("读取文件中...");
}

main();
// 输出:
// 打开文件: data.txt
// 读取文件中...
// 关闭文件: data.txt

⚡ 当 main() 执行完毕时,f 离开作用域,系统会自动调用 f[Symbol.dispose]()


异步版本

javascript 复制代码
class AsyncResource {
  async [Symbol.asyncDispose]() {
    console.log("异步释放资源...");
    await new Promise(res => setTimeout(res, 1000));
    console.log("资源已释放");
  }
}

async function run() {
  await using r = new AsyncResource();
  console.log("使用异步资源中...");
}

run();

三、内部机制简析

  • using词法作用域绑定 (和 let/const 类似)。

  • 离开作用域时:

    • 如果是同步资源 → 调用 [Symbol.dispose]()
    • 如果是异步资源 → 调用 [Symbol.asyncDispose]()
  • 它可以和 trycatchfinally 一起安全使用。


四、使用场景

场景 传统做法 新写法(using)
文件操作 try/finally 手动关闭 自动调用 dispose
数据库连接 手动断开 自动释放连接
临时锁 try/finally 释放锁 离开作用域自动释放

五、注意事项

  1. using 只能在模块或函数作用域中使用,不能在全局作用域直接声明。
  2. 不能与 var 共用。
  3. 不会影响 GC(垃圾回收),它只是提供结构化释放资源的机制。

六、实践demo------简化revokeObjectURL

在图片回显/文件下载等场景中,开发很容易忘记revokeObjectURL,我们可以利用using特性进行封装,消除revokeObjectURL的心智负担。

就比如上传图片后回显的操作:

js 复制代码
console.log('🚀 using demo 启动...');

function objectURLResource(blob) {
    const url = URL.createObjectURL(blob);
    console.log('🆕 创建 URL:', url);
    return {
        url,
          [Symbol.dispose]() {
            console.log("释放资源...");
            URL.revokeObjectURL(url);
            console.log('🧹 自动释放 URL:', url);
            
        },
        // [Symbol.dispose]() {
        //     console.log("释放资源...");
        //     new Promise(resolve => setTimeout(resolve, 1000)).then(()=>{
        //         URL.revokeObjectURL(url);
        //         console.log('🧹 自动释放 URL:', url);
        //     })
            
        // },
          async [Symbol.asyncDispose]() {
            console.log("异步释放资源...");
            await new Promise(resolve => setTimeout(resolve, 3000)); // 模拟异步操作
            URL.revokeObjectURL(url);
            console.log('🧹 自动释放 URL:', url);
        },
    };
}



async function showImage(blob) {
    // await using resource = objectURLResource(blob);
    using resource = objectURLResource(blob);
    const img = document.createElement('img');
    img.src = resource.url;
    img.alt = 'demo';
    const { resolve, promise } = Promise.withResolvers()
    img.onload = () => {
        resolve()
    }
    document.body.appendChild(img);
    await promise;
    console.log('✅ 演示完毕,URL 将被自动 revoke');
}
document.querySelector('#file').addEventListener('change',async (e) => {
    const f = e.target.files[0]
   await showImage(f);
   console.log('do others...    ')

})

revokeObjectURL的注意事项

当img加载后,执行revokeObjectURL并不会影响内容显示,但如果img未加载完就执行了revokeObjectURL,则无法显示图片。因此以上demo中增加了await promise的处理,确保图片被显示出来。

那么可能会有人问,此处能否用Symbol.asyncDispose来解决该问题?答案是不能。但这是一个很好的问题,触及到了 Symbol.asyncDisposeSymbol.dispose的根本区别.

七、Symbol.asyncDispose与Symbol.dispose的区别

表面上看,以上demo中如果写成await using resource = objectURLResource(blob);,释放资源用的是Symbol.asyncDispose,如果不加await,则调用Symbol.dispose

异步释放:

那么这两个api的使用场景分别是啥呢?我们不妨在同步释放中写下如下代码:

javascript 复制代码
[Symbol.dispose]() {
    console.log("释放资源...");
    new Promise(resolve => setTimeout(resolve, 1000)).then(()=>{
        URL.revokeObjectURL(url);
        console.log('🧹 自动释放 URL:', url);
    })
},

可以看到,do others...在释放完成前就执行了。

因此可以理解:Symbol.asyncDispose是为了让后续的代码等待异步释放完成后再执行,因为有些释放场景,可能需要进行io或其他异步校验,而Symbol.dispose释放过程是同步的,后续代码执行时可以认为资源已经被释放了。如果后续代码执行时并不关心该资源是否已经释放了,那使用Symbol.dispose即可。

相关推荐
集成显卡13 小时前
Lucide Icons:一套现代、轻量且可定制的 SVG 图标库
前端·ui·图标库·lucide
pas13613 小时前
37-mini-vue 解析插值
前端·javascript·vue.js
十里-14 小时前
vue.js 2前端开发的项目通过electron打包成exe
前端·vue.js·electron
雨季66615 小时前
构建 OpenHarmony 简易文字行数统计器:用字符串分割实现纯文本结构感知
开发语言·前端·javascript·flutter·ui·dart
小北方城市网15 小时前
Redis 分布式锁高可用实现:从原理到生产级落地
java·前端·javascript·spring boot·redis·分布式·wpf
console.log('npc')15 小时前
vue2 使用高德接口查询天气
前端·vue.js
2401_8920005215 小时前
Flutter for OpenHarmony 猫咪管家App实战 - 添加支出实现
前端·javascript·flutter
天马379815 小时前
Canvas 倾斜矩形绘制波浪效果
开发语言·前端·javascript
天天向上102416 小时前
vue3 实现el-table 部分行不让勾选
前端·javascript·vue.js
qx0916 小时前
esm模块与commonjs模块相互调用的方法
开发语言·前端·javascript