#wasi #worker #browser #service #web-apps #run-wasm

wasi-worker

浏览器服务工作者(service worker)的 WASM/WASI 接口

9 个版本

0.5.0 2020 年 2 月 7 日
0.4.0 2019 年 11 月 18 日
0.3.6 2019 年 11 月 7 日
0.3.2 2019 年 10 月 30 日

#819WebAssembly


用于 wasi-worker-yew

MIT/Apache

14KB
136

创建 WASM 浏览器服务工作者的正确方法。

此 crate 提供了 rust 库和 JS 粘合代码,允许将 POSIX 兼容的代码包装成 WASM/WASI 目标,以便在浏览器服务工作者中运行。它还提供了与主 Web 应用的输入/输出消息通道。

为什么选择 WASI?

WebAssembly 系统接口(WASI)是一个令人兴奋的新规范,它允许使用 WebAssembly 在任何地方安全、安全地运行 POSIX 类似的应用程序。 -> Medium / 使用 Wasmer-JS 在 JavaScript 中运行 WASI

编译到 WASI 的 POSIX 兼容应用程序现在也可以在浏览器中运行,考虑到代码重用和将服务器工作负载委托给客户端。此外,编译到 wasm32-wasi 目标代码的执行速度似乎比编译到其他具有 Web 绑定的 wasm32 目标代码快约 2 倍,因此 CPU 密集型工作负载现在可以以接近本地目标的性能执行。

我为什么需要 wasi-worker?

作为 Web 应用程序一部分执行的自定义 WASM 代码占用相同的 JavaScript 线程,因此如果 WASM 代码正在运行复杂的计算,它将在工作时阻止浏览器应用程序。为了使其在单独的线程中工作,我们可以使用 浏览器服务工作者

如前所述,编译到 WASI 的代码似乎运行速度快约 2 倍(链接到基准测试)。唯一的问题是 WASI 并非专为从浏览器执行而构建,而是一个旨在在服务器端运行 WASM 代码的标准。利用 @wasmer/wasi,此 crate 提供了浏览器服务工作者 WASI 运行时以及与 Web 应用程序之间的通信桥梁。

使用示例

此示例需要 WASI JavaScript 绑定 它可以通过 wasi-worker-cli 部署WASI 环境 以及正确预配置的文件系统

use wasi_worker::*;

struct MyWorker;
impl Handler for MyWorker {
  fn on_message(&self, msg: &[u8]) -> std::io::Result<()> {
    println!("My Worker got message: {:?}", msg);
    Ok(())
  }
}

fn main() {
  // JS glue code will hook to /output.bin
  ServiceWorker::initialize(ServiceOptions::default());
  ServiceWorker::set_message_handler(Box::new(MyWorker {}));
  // Send binary message to main browser application
  ServiceWorker::post_message(b"message");
}

// Function will be called from JS on incoming message
#[no_mangle]
pub extern "C" fn message_ready() -> usize {
  ServiceWorker::on_message()
    .expect("ServiceWorker.on_message")
}

JavaScript WASI 绑定

JavaScript WASI 绑定建立在 @wasmer 库之上,可以使用 wasiworker 工具轻松部署

cargo install wasi-worker-cli

wasiworker install 将添加示例 worker 代码和 bin 目标到当前 crate 目录

wasiworker install

wasiworker deploy 将构建 worker 二进制目标,并在 ./dist 下使用 JS 粘合代码进行部署

wasiworker deploy

要修改 JS 粘合代码源代码,位于同一仓库。

更详细的示例

use wasi_worker::*;

struct MyWorker {}
impl Handler for MyWorker {
  fn on_message(&self, msg: &[u8]) -> std::io::Result<()> {
    // Process incoming message
    println!("My Worker got message: {:?}", msg);
    Ok(())
  }
}

fn main() {
  // In WASI setup output will go to /output.bin
  #[cfg(target_os="wasi")]
  let opt = ServiceOptions::default();
  // In user filesystem we operate under current dir
  #[cfg(not(target_os="wasi"))]
  let opt = ServiceOptions { 
    output: FileOptions::File("./testdata/output.bin".to_string()) 
  };
  let output_file = match &opt.output { 
    FileOptions::File(path) => path.clone() 
  };
  ServiceWorker::initialize(opt)
    .expect("ServiceWorker::initialize");

  // Attach Agent to ServiceWorker as message handler singleton
  ServiceWorker::set_message_handler(Box::new(MyWorker {}));

  // Send binary message to main browser application
  // this requires JS glue see wasi-worker-cli
  ServiceWorker::post_message(b"message")
    .expect("ServiceWorker::post_message");

  // It does not autodelete output file
  std::fs::remove_file(output_file)
    .expect("Remove output.bin");
}

待办事项

  • 带有 WASI fs 接口的库代码
  • 基本示例
  • 文档
  • worker 设置的 CLI 工具
  • 退出时丢弃输出文件

无运行时依赖