Skip to content

Commit 6fafe5e

Browse files
committed
Implement extern "C" async functions.
It converts a JS Promise into a wasm_bindgen_futures::JsFuture that implements Future<Result<JsValue, JsValue>>.
1 parent 1edd43a commit 6fafe5e

File tree

3 files changed

+70
-13
lines changed

3 files changed

+70
-13
lines changed

crates/backend/src/codegen.rs

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -971,22 +971,52 @@ impl TryToTokens for ast::ImportFunction {
971971
);
972972
}
973973
Some(ref ty) => {
974-
abi_ret = quote! {
975-
<#ty as wasm_bindgen::convert::FromWasmAbi>::Abi
976-
};
977-
convert_ret = quote! {
978-
<#ty as wasm_bindgen::convert::FromWasmAbi>
979-
::from_abi(#ret_ident)
980-
};
974+
if self.function.r#async {
975+
abi_ret = quote! { <js_sys::Promise as wasm_bindgen::convert::FromWasmAbi>::Abi };
976+
let future = quote! {
977+
wasm_bindgen_futures::JsFuture::from(
978+
<js_sys::Promise as wasm_bindgen::convert::FromWasmAbi>
979+
::from_abi(#ret_ident)
980+
).await
981+
};
982+
convert_ret = if self.catch {
983+
quote! { Ok(#future?) }
984+
} else {
985+
quote! { #future.expect("unexpected exception") }
986+
};
987+
} else {
988+
abi_ret = quote! {
989+
<#ty as wasm_bindgen::convert::FromWasmAbi>::Abi
990+
};
991+
convert_ret = quote! {
992+
<#ty as wasm_bindgen::convert::FromWasmAbi>
993+
::from_abi(#ret_ident)
994+
};
995+
}
981996
}
982997
None => {
983-
abi_ret = quote! { () };
984-
convert_ret = quote! { () };
998+
if self.function.r#async {
999+
abi_ret = quote! { <js_sys::Promise as wasm_bindgen::convert::FromWasmAbi>::Abi };
1000+
let future = quote! {
1001+
wasm_bindgen_futures::JsFuture::from(
1002+
<js_sys::Promise as wasm_bindgen::convert::FromWasmAbi>
1003+
::from_abi(#ret_ident)
1004+
).await
1005+
};
1006+
convert_ret = if self.catch {
1007+
quote! { #future?; Ok(()) }
1008+
} else {
1009+
quote! { #future.expect("uncaught exception"); }
1010+
};
1011+
} else {
1012+
abi_ret = quote! { () };
1013+
convert_ret = quote! { () };
1014+
}
9851015
}
9861016
}
9871017

9881018
let mut exceptional_ret = quote!();
989-
if self.catch {
1019+
if self.catch && !self.function.r#async {
9901020
convert_ret = quote! { Ok(#convert_ret) };
9911021
exceptional_ret = quote! {
9921022
wasm_bindgen::__rt::take_last_exception()?;
@@ -1045,12 +1075,17 @@ impl TryToTokens for ast::ImportFunction {
10451075
&self.rust_name,
10461076
);
10471077

1078+
let maybe_async = if self.function.r#async {
1079+
Some(quote!{async})
1080+
} else {
1081+
None
1082+
};
10481083
let invocation = quote! {
10491084
#(#attrs)*
10501085
#[allow(bad_style)]
10511086
#[doc = #doc_comment]
10521087
#[allow(clippy::all)]
1053-
#vis fn #rust_name(#me #(#arguments),*) #ret {
1088+
#vis #maybe_async fn #rust_name(#me #(#arguments),*) #ret {
10541089
#extern_fn
10551090

10561091
unsafe {

guide/src/reference/js-promises-and-rust-futures.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,28 @@ Here we can see how converting a `Promise` to Rust creates a `impl Future<Output
2525
= Result<JsValue, JsValue>>`. This corresponds to `then` and `catch` in JS where
2626
a successful promise becomes `Ok` and an erroneous promise becomes `Err`.
2727

28+
You can also import a JS async function directly with a `extern "C"` block.
29+
For now, the return type must be `JsValue` or no return at all
30+
31+
```rust
32+
extern "C" {
33+
async fn asyncFunc1() -> JsValue;
34+
async fn asyncFunc2();
35+
}
36+
```
37+
38+
The `async` can be combined with the `catch` attribute to manage errors from the
39+
JS promise:
40+
41+
```rust
42+
extern "C" {
43+
#[wasm_bindgen(catch)]
44+
async fn asyncFunc3() -> Result<JsValue, JsValue>;
45+
#[wasm_bindgen(catch)]
46+
async fn asyncFunc4() -> Result<(), JsValue>;
47+
}
48+
```
49+
2850
Next up you'll probably want to export a Rust function to JS that returns a
2951
promise. To do this you can use an `async` function and `#[wasm_bindgen]`:
3052

tests/wasm/futures.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ use wasm_bindgen_test::*;
33

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

99
#[wasm_bindgen_test]
1010
async fn smoke() {
11-
wasm_bindgen_futures::JsFuture::from(call_exports())
11+
call_exports()
1212
.await
1313
.unwrap();
1414
}

0 commit comments

Comments
 (0)