如何在 node.js 环境中从 WebAssembly (Rust) 调用异步 JavaScript 导入函数?

How to call an async JavaScript Import Function from WebAssembly (Rust) in a node.js environment?

让我们考虑一个如下所示的示例导入对象:

const importObject = {
  exampleAsyncImportFunction: async () => fs.readFile("./someExampleFile.txt", "utf-8") // returns Promise<String>
};

我想在我的 rust 编写的 WASM 模块中使用它,类似于:

#[wasm_bindgen]
extern "C" {
  pub async fn exampleAsyncImportFunction() -> JsValue; // Importing the JS-Function
}

#[wasm_bindgen]
pub async fn exampleExportFunction() -> Result<JsValue, JsValue> {
  let theJSPromise = exampleAsyncImportFunction(); // Call the async import function
  let promiseResult = theJSPromise.await; // Execute the async import function
  // Do sth with the result
  OK(JsValue::UNDEFINED)
}

不幸的是,这会导致无法访问的错误。有趣的是,当 JavaScript-Function returns 例如一个字符串,像这样:

const importObject = {
  exampleAsyncImportFunction: async () => "Some dummy content" // returns Promise<String> as well
};

当然,异步导入函数应该执行一些实际的异步任务,否则使用同步函数会更好。

我尝试做一些研究,发现 'js_sys::Promise' 代表 Rust 中的 JS-Promise,而 'wasm_bindgen_futures::JsFuture' 能够以某种方式将 JS-Promise 转换为 Rust -未来。但我不明白,if/how 这些类型可能有助于解决问题以及在这种情况下通常应如何使用它们。

此外,JS-Import-Function 的 return 类型(声明为 'JsValue')似乎以某种方式实现了 'Future'-Trait .据我所知,因此 'awaiting' 结果是可能的。但我很困惑,实际的底层类型是什么(JsFuture、Promise、其他……)。

我希望有人能帮助我解决这个问题,解决实际问题,同时解释一下所有类型之间的关系(尤其是关于 JsValue)。

提前致谢!

我自己找到了解决方案,由于网上其他地方似乎没有好的信息,我想把我的解决方案分享给有类似问题的人。

对于导入 JavaScript-Function,尽管它是异步的,但重要的是表现得好像函数是同步的。这样做的原因是,Rust 中的异步函数仅在使用执行程序或等待时执行。因此,async-JS-Function 是一个同步函数,returns 一个 Promise 可以被同步使用。然后可以显式调用返回的承诺。

导入部分将如下所示:

#[wasm_bindgen(module = "/some/file.js")]
extern "C" {
  pub fn exampleAsyncImportFunction() -> JsValue; // JsValue <==> Promise
  // Instead of:
  // pub async fn exampleAsyncImportFunction() -> Result<JsValue, JsValue>
}

要使用导入函数,必须将作为 JsValue 的 Promise 转换为 js_sys::Promise,然后再转换为 wasm_bindgen_futures::JsFuture。之后就可以正常等待了

原始问题中的示例导出函数如下所示:

#[wasm_bindgen]
pub async fn exampleExportFunction() {
  // Get a reference to the import function as a 'JsValue'
  let promiseAsJsValue = exampleAsyncImportFunction(); // No execution, yet

  // Convert 'JsValue' to 'js_sys::Promise', which is a proxy-type for JS-Promises
  let promise = js_sys::Promise::from(promiseAsJsValue);

  // Convert 'js_sys::Promise' to 'wasm_bindgen_future::JsFuture'
  let future = wasm_bindgen_future::JsFuture::from(promise);

  // Actually execute the Promise/Future
  let result: Result<JsValue, JsValue> = future.await;

  // Interpret the result
  if let Ok(content) = result {
    // do sth with the content
  }
}

我希望这种方法,特别是从 JsValue 到 JsFuture 的转换,能对你们中的一些人有所帮助,因为官方文档没有涵盖该用例,而只是手动实例化一个 js_sys::Promise(例如 Promise::resolve(&"foo".into()) )或使用 web_sys::window::fetch().