使用 JS `Promise` 和 Rust `Future`

网络上的许多 API 都使用 `Promise`,例如 JS 中的 `async` 函数。自然地,您可能希望从 Rust 中与它们进行交互!为此,您可以使用 `wasm-bindgen-futures` crate 以及 Rust `async` 函数。

您可能会遇到的第一个问题是需要使用 `Promise`。为此,您需要使用 js_sys::Promise。获得这些值后,您可以将这些值转换为 `wasm_bindgen_futures::JsFuture`。此类型实现了 `std::future::Future` 特性,允许您在 `async` 函数中自然地使用它。例如


# #![allow(unused_variables)]
#fn main() {
async fn get_from_js() -> Result<JsValue, JsValue> {
    let promise = js_sys::Promise::resolve(&42.into());
    let result = wasm_bindgen_futures::JsFuture::from(promise).await?;
    Ok(result)
}
#}

在这里,我们可以看到如何将 `Promise` 转换为 Rust 创建一个 `impl Future<Output = Result<JsValue, JsValue>>`。这对应于 JS 中的 `then` 和 `catch`,其中成功的承诺变为 `Ok`,错误的承诺变为 `Err`。

您还可以使用 `extern "C"` 块直接导入 JS 异步函数,并且承诺将自动转换为未来。目前,返回类型必须是 `JsValue` 或根本没有返回


# #![allow(unused_variables)]
#fn main() {
#[wasm_bindgen]
extern "C" {
    async fn async_func_1() -> JsValue;
    async fn async_func_2();
}
#}

`async` 可以与 `catch` 属性结合使用来管理来自 JS 承诺的错误


# #![allow(unused_variables)]
#fn main() {
#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(catch)]
    async fn async_func_3() -> Result<JsValue, JsValue>;
    #[wasm_bindgen(catch)]
    async fn async_func_4() -> Result<(), JsValue>;
}
#}

接下来,您可能希望将 Rust 函数导出到 JS,该函数返回一个承诺。为此,您可以使用 `async` 函数和 `#[wasm_bindgen]`


# #![allow(unused_variables)]
#fn main() {
#[wasm_bindgen]
pub async fn foo() {
    // ...
}
#}

当从 JS 调用时,这里的 `foo` 函数将返回一个 `Promise`,因此您可以将其导入为

import { foo } from "my-module";

async function shim() {
    const result = await foo();
    // ...
}

`async fn` 的返回值

在 Rust 中使用 `async fn` 并将其导出到 JS 时,对返回类型有一些限制。导出的 Rust 函数的返回值最终将变为 `Result<JsValue, JsValue>`,其中 `Ok` 变为成功解析的承诺,`Err` 等效于抛出异常。

以下类型作为 `async fn` 的返回值类型受支持

  • () - 变为 JS 中成功的 `undefined`
  • T: Into<JsValue> - 变为成功的 JS 值
  • Result<(), E: Into<JsValue>> - 如果 Ok(()) 转换为成功的 undefined,否则转换为失败的 Promise,并将 E 转换为 JS 值。
  • Result<T: Into<JsValue>, E: Into<JsValue>> - 与上例类似,只是两个数据负载都转换为 JsValue

请注意,许多类型都实现了转换为 JsValue,例如通过 #[wasm_bindgen] 导入的所有类型(即 js-sysweb-sys 中的类型)、u32 等基本类型以及所有导出的 #[wasm_bindgen] 类型。一般来说,您应该能够编写代码而无需进行太多显式转换,宏会处理其余部分!

使用 wasm-bindgen-futures

wasm-bindgen-futures crate 弥合了 JavaScript Promise 和 Rust Future 之间的差距。它的 JsFuture 类型提供了从 JavaScript Promise 转换为 Rust Future 的转换,而它的 future_to_promise 函数将 Rust Future 转换为 JavaScript Promise 并安排它驱动到完成。

了解更多

Future 版本的兼容性

crates.io 上的当前 crate,wasm-bindgen-futures 0.4.*,支持 Rust 中的 std::future::Futureasync/await。这通常需要 Rust 1.39.0+(截至 2019-09-05 在撰写本文时,它是 Rust 的 nightly 频道)。

如果您使用的是 futures 0.1.* crate 中的 Future 特性,那么您需要使用 crates.io 上的 wasm-bindgen-futures0.3.* 版本。