-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for #[wasm_bindgen]
on async fn
#1754
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! r=me with @Pauan's feedback addressed
This commit adds support to attach `#[wasm_bindgen]` on an `async fn` which will change the return value into a `Promise` in JS. This in theory has the exact same semantics as an `async` function in JS where you call it with all the arguments, nothing happens and you get a promise back, and then later the promise actually resolves. This commit also adds a helper trait, `IntoJsResult`, to allow `async` functions with multiple kinds of return values instead of requiring everything to be `Result<JsValue, JsValue>`.
6dc1308
to
b73b326
Compare
Add support for `#[wasm_bindgen]` on `async fn` (rustwasm#1754)
Is this a known issue? use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Foo{
}
#[wasm_bindgen]
impl Foo {
pub async fn bar(&self) -> Result<i32,JsValue> {
Ok(42)
}
}
[dependencies]
wasm-bindgen = "0.2.51"
wasm-bindgen-futures = "0.4.1" |
@alexlapa although not a great error message, that's a legitimate error message because futures shared with JS need to have the |
I see. Is it possible to make functions like this valid?
Or to add some kind of |
@alexlapa that should work yeah! For that though you'll have to return |
As for automatically doing glue, I'm not entirely sure how it would work, but PRs and ideas are always welcome! |
@alexlapa That isn't an issue with wasm-bindgen, it's an issue with how Rust Futures work in general: Because Futures are heap allocated and run in the background, they cannot contain stack references. And Instead, you should take If you need to access Note: most of the time you can use stack references with async/await, it is only spawning that has the |
@Pauan , Thanks for detailed explanation. So there is no way to
It would be awesome if I can implement this feature, if it makes sense :) |
That's fairly easy to workaround by simply having two methods: impl Foo {
pub async fn new_() -> Result<Foo, JsValue> {
Ok(Foo)
}
}
#[wasm_bindgen]
impl Foo {
pub fn new() -> Promise {
future_to_promise(Self::new_())
}
} Now you can test the This is a common technique when using wasm-bindgen, because sometimes you need to do some type conversions when sending to JS, so that's what the above allows you to do.
That already is valid. The problem is that (Note: the above is a Rust error, it doesn't even have any wasm-bindgen code). You can specify a method using impl Foo {
pub async fn bar(&'static self) { ... }
} ...but now you can only put These restrictions aren't caused by wasm-bindgen, they're caused by lifetimes in Rust, which is necessary to prevent undefined behavior. So it can't be "fixed" by wasm-bindgen, because "fixing" it would cause undefined behavior. Like I said, the solution is to not use But if you avoid references (i.e. you use But... if you can't use references, what if you need to access |
@Pauan Is there a complete example of using self: RC < self > to call its own method |
@shijunti19 I don't think there are any examples, but it's just standard Rust: struct Inner {
value: i32,
}
struct Foo {
inner: RefCell<Inner>,
}
impl Foo {
fn new(value: i32) -> Rc<Self> {
Rc::new(Self {
inner: RefCell::new(Inner { value }),
})
}
async fn increment(self: Rc<Self>) {
let mut borrow = self.inner.borrow_mut();
borrow.value += 1;
}
} let x = Foo::new(5);
spawn_local(async move {
x.increment().await;
}); Make sure that you never use let mut borrow = self.inner.borrow_mut();
// Very bad, the borrow is still alive during the .await, don't do this
some_async().await; {
let mut borrow = self.inner.borrow_mut();
}
// This is okay, since it drops the borrow before the .await
some_async().await;
{
// It can reborrow after the .await is finished
let mut borrow = self.inner.borrow_mut();
} |
@Pauan thanks for your example. I tried to combine this with wasm-bindgen but unfortunately that does not work. use std::{cell::RefCell, rc::Rc};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Wrapper {
inner: RefCell<InnerCore>,
}
pub struct InnerCore {
val: u32,
}
// TODO:
// Using `#[wasm_bindgen]` does not compile:
//
// the trait `IntoWasmAbi` is not implemented for `std::rc::Rc<Wrapper>`
//
// #[wasm_bindgen]
impl Wrapper {
//#[wasm_bindgen(constructor)]
pub fn new() -> Rc<Wrapper> {
Rc::new(Self {
inner: RefCell::new(InnerCore { val: 33 }),
})
}
pub async fn method_one(self: Rc<Wrapper>, _: u32) -> Result<JsValue, JsValue> {
todo!()
}
pub async fn method_two(self: Rc<Wrapper>, _: String) -> Result<JsValue, JsValue> {
todo!()
}
} Do you have any idea how to solve this? |
@flosse You have to move the #[wasm_bindgen]
#[derive(Clone)]
pub struct Wrapper {
inner: Rc<RefCell<InnerCore>>,
} |
This commit adds support to attach
#[wasm_bindgen]
on anasync fn
which will change the return value into a
Promise
in JS. This intheory has the exact same semantics as an
async
function in JS whereyou call it with all the arguments, nothing happens and you get a
promise back, and then later the promise actually resolves.
This commit also adds a helper trait,
IntoJsResult
, to allowasync
functions with multiple kinds of return values instead of requiring
everything to be
Result<JsValue, JsValue>
.