使用 JS 的 Promise 和 Rust 的 Future

Web 上許多 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)]
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 中的 thencatch,其中成功的 promise 會變成 Ok,而錯誤的 promise 會變成 Err

您也可以使用 extern "C" 區塊直接匯入 JS 非同步函式,並且 promise 會自動轉換為 future。目前,回傳型別必須是 JsValue 或完全沒有回傳值。

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

async fn get_from_js() -> f64 {
    async_func_1_ret_number().await.as_f64().unwrap_or(0.0)
}
}

async 可以與 catch 屬性結合使用,以管理來自 JS promise 的錯誤

#![allow(unused)]
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,該函式會回傳 promise。為此,您可以使用 async 函式和 #[wasm_bindgen]

#![allow(unused)]
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 會變成成功解析的 promise,而 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.* 版本。