Skip to content

Commit

Permalink
Implement extern "C" async functions.
Browse files Browse the repository at this point in the history
It converts a JS Promise into a wasm_bindgen_futures::JsFuture that
implements Future<Result<JsValue, JsValue>>.
  • Loading branch information
rodrigorc committed Jun 20, 2020
1 parent 1edd43a commit 6fafe5e
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 13 deletions.
57 changes: 46 additions & 11 deletions crates/backend/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -971,22 +971,52 @@ impl TryToTokens for ast::ImportFunction {
);
}
Some(ref ty) => {
abi_ret = quote! {
<#ty as wasm_bindgen::convert::FromWasmAbi>::Abi
};
convert_ret = quote! {
<#ty as wasm_bindgen::convert::FromWasmAbi>
::from_abi(#ret_ident)
};
if self.function.r#async {
abi_ret = quote! { <js_sys::Promise as wasm_bindgen::convert::FromWasmAbi>::Abi };
let future = quote! {
wasm_bindgen_futures::JsFuture::from(
<js_sys::Promise as wasm_bindgen::convert::FromWasmAbi>
::from_abi(#ret_ident)
).await
};
convert_ret = if self.catch {
quote! { Ok(#future?) }
} else {
quote! { #future.expect("unexpected exception") }
};
} else {
abi_ret = quote! {
<#ty as wasm_bindgen::convert::FromWasmAbi>::Abi
};
convert_ret = quote! {
<#ty as wasm_bindgen::convert::FromWasmAbi>
::from_abi(#ret_ident)
};
}
}
None => {
abi_ret = quote! { () };
convert_ret = quote! { () };
if self.function.r#async {
abi_ret = quote! { <js_sys::Promise as wasm_bindgen::convert::FromWasmAbi>::Abi };
let future = quote! {
wasm_bindgen_futures::JsFuture::from(
<js_sys::Promise as wasm_bindgen::convert::FromWasmAbi>
::from_abi(#ret_ident)
).await
};
convert_ret = if self.catch {
quote! { #future?; Ok(()) }
} else {
quote! { #future.expect("uncaught exception"); }
};
} else {
abi_ret = quote! { () };
convert_ret = quote! { () };
}
}
}

let mut exceptional_ret = quote!();
if self.catch {
if self.catch && !self.function.r#async {
convert_ret = quote! { Ok(#convert_ret) };
exceptional_ret = quote! {
wasm_bindgen::__rt::take_last_exception()?;
Expand Down Expand Up @@ -1045,12 +1075,17 @@ impl TryToTokens for ast::ImportFunction {
&self.rust_name,
);

let maybe_async = if self.function.r#async {
Some(quote!{async})
} else {
None
};
let invocation = quote! {
#(#attrs)*
#[allow(bad_style)]
#[doc = #doc_comment]
#[allow(clippy::all)]
#vis fn #rust_name(#me #(#arguments),*) #ret {
#vis #maybe_async fn #rust_name(#me #(#arguments),*) #ret {
#extern_fn

unsafe {
Expand Down
22 changes: 22 additions & 0 deletions guide/src/reference/js-promises-and-rust-futures.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,28 @@ Here we can see how converting a `Promise` to Rust creates a `impl Future<Output
= Result<JsValue, JsValue>>`. This corresponds to `then` and `catch` in JS where
a successful promise becomes `Ok` and an erroneous promise becomes `Err`.

You can also import a JS async function directly with a `extern "C"` block.
For now, the return type must be `JsValue` or no return at all

```rust
extern "C" {
async fn asyncFunc1() -> JsValue;
async fn asyncFunc2();
}
```

The `async` can be combined with the `catch` attribute to manage errors from the
JS promise:

```rust
extern "C" {
#[wasm_bindgen(catch)]
async fn asyncFunc3() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
async fn asyncFunc4() -> Result<(), JsValue>;
}
```

Next up you'll probably want to export a Rust function to JS that returns a
promise. To do this you can use an `async` function and `#[wasm_bindgen]`:

Expand Down
4 changes: 2 additions & 2 deletions tests/wasm/futures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ use wasm_bindgen_test::*;

#[wasm_bindgen(module = "tests/wasm/futures.js")]
extern "C" {
fn call_exports() -> js_sys::Promise;
async fn call_exports() -> Result<JsValue, JsValue>;
}

#[wasm_bindgen_test]
async fn smoke() {
wasm_bindgen_futures::JsFuture::from(call_exports())
call_exports()
.await
.unwrap();
}
Expand Down

0 comments on commit 6fafe5e

Please sign in to comment.