写过 Rust 写进浏览器的代码,你会发现工具链越来越薄——WASI 0.3 把这件事彻底变了

RustWebAssembly,这几年最痛苦的点不是语言本身,而是”跑起来”的路径太重:wasm-pack 配 wasm-bindgen,NPM 包装一堆粘合层,调试靠浏览器扩展,转译路径长到每次配完都要深呼吸一口气。WASI 0.3 把这个路径大幅缩短了,其中最关键的是 Async Components 模型终于落地。

发生了什么

WASI(WebAssembly System Interface)0.3 是 WASI 标准的一次大版本迭代,核心改动是正式引入了异步组件模型——WebAssembly 组件可以声明和调用异步函数,而不需要自己在 JS 层手写一堆 promise 桥接代码。

之前 wasm-bindgen 的异步方案本质上是”Rust 输出同步函数,JS 侧负责包装成 promise”,这意味着 Rust 侧不知道调用的实际是异步的,错误处理和资源管理都很别扭。Async Components 把这个抽象做进了 WASI 层面,Rust 代码可以直接声明 async fn,工具链负责生成正确的组件接口。

// Rust 侧直接写 async 函数
#[wasm_bindgen]
pub async fn fetch_data(url: &str) -> Result<String, JsValue> {
    // ...
}

现在 wasm-bindgen 配合 WASI 0.3 工具链,可以把这个 async 声明直接映射到组件接口,调用方拿到的是真正的异步函数而不是伪装成同步的 thunk。

为什么这很关键

如果你用过 wasm-pack + wasm-bindgen 构建真实项目,最大的痛感是”异步边界”问题:Rust 侧无法直接处理 Promise,JS 侧必须知道哪些调用是异步的。这个认知割裂在复杂项目里会造成大量”粘合代码”。

WASI 0.3 的 Async Components 把这个边界往上推了一层——组件接口定义里直接包含异步语义,调用方工具链负责生成对应的 Promise wrapper。这意味着 Rust 开发者可以专注于业务逻辑,不需要在脑子里维护”这里要包 promise,那里要 awaited”的映射表。

另外 WASI 0.3 配套的 WASI interface types 改进让组件之间的数据类型交换更干净,不需要手写复杂的序列化逻辑。

工具链现状

WASI 0.3 的工具链支持在 wasmtime、wasm-pack 和 wit-bindgen 里都有了实质性进展。wasmtime 7.0+ 对 WASI 0.3 有完整支持,wasm-pack 的 nightly 版本已经可以生成 WASI 0.3 风格的组件。

不过需要注意:生产项目迁移到 WASI 0.3 需要确认你的部署目标(浏览器 / Node.js / Deno / 服务端)都支持 WASI 0.3 组件模型。Node.js 的 WASM WASI 支持走得比较慢,部分运行时功能可能需要 polyfill。

下一步

如果你现在有用 wasm-pack 写的 wasm 模块,可以用一个简单的方式感受 WASI 0.3 的区别:升级 wasm-pack 到 nightly,生成一个新组件接口看看 wit 文件里 async 语义的表达方式。对于新项目,直接从 async fn 入手会比先写同步再迁移顺畅很多。工具链每个月都在更新,建议关注字节跳动的 wasmexperiment 或者 Figma 的 wasm 相关开源项目,它们的 CI 配置通常是最及时的参考。

评论区

0 条评论

登录后可评论。

铁锈·Rust工具链 103 阅读