I am following a guide for setting up a WebRTC data-channel with web-sys. I can copy and paste the code and it compiles correctly. The start() function is async which makes it possible to await a JsFuture inside the main scope, however I am trying to move this await to the onmessage_callback
block instead. Just by adding this one line to the original implementation I have this:
let onmessage_callback =
Closure::wrap(
Box::new(move |ev: MessageEvent| {
let desc = JsFuture::from(pc1.create_offer()).await.unwrap();
match ev.data().as_string() {
Some(message) => {
console_warn!("{:?}", message);
dc1_clone.send_with_str("Pong from pc1.dc!").unwrap();
}
None => {}
}
}) as Box<dyn FnMut(MessageEvent)>,
);
dc1.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
onmessage_callback.forget();
Once I compile this I ofcourse get an error saying that awaiting the future desc
is only possible inside an asynchronous context. I figured that I could add the async
keyword when defining the FnMut
function:
let onmessage_callback =
Closure::wrap(
Box::new(move |ev: MessageEvent| async { // <-- async
let desc = JsFuture::from(pc1.create_offer()).await.unwrap();
match ev.data().as_string() {
Some(message) => {
console_warn!("{:?}", message);
dc1_clone.send_with_str("Pong from pc1.dc!").unwrap();
}
None => {}
}
}) as Box<dyn FnMut(MessageEvent)>,
);
dc1.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
onmessage_callback.forget();
But when I compile this I get the following error:
error[E0271]: type mismatch resolving `<[closure@src/lib.rs:48:22: 57:14] as FnOnce<(MessageEvent,)>>::Output == ()`
--> src/lib.rs:48:13
|
48 | / Box::new(move |ev: MessageEvent| async {
49 | | let desc = JsFuture::from(pc1.create_offer()).await.unwrap();
50 | | match ev.data().as_string() {
51 | | Some(message) => {
... |
56 | | }
57 | | }) as Box<dyn FnMut(MessageEvent)>,
| |______________^ expected `()`, found opaque type
|
= note: expected unit type `()`
found opaque type `impl Future<Output = [async output]>`
= note: required for the cast to the object type `dyn FnMut(MessageEvent)`
I am not sure how to proceed with this, I think the error is saying that the callback returns a future but it should be void instead.
How do I define an async callback?
The reason is as specified by @nikoss, you pass a future-returning function and cast it to a unit-returning function.
As for how to solve that, you can spawn the future on JS promise microtasks queue with spawn_local()
:
let (pc1_clone, dc1_clone) = (pc1.clone(), dc1.clone());
let onmessage_callback =
Closure::wrap(
Box::new(move |ev: MessageEvent| {
wasm_bindgen_futures::spawn_local(async move {
let desc = JsFuture::from(pc1_clone.create_offer()).await.unwrap();
match ev.data().as_string() {
Some(message) => {
console_warn!("{:?}", message);
dc1_clone.send_with_str("Pong from pc1.dc!").unwrap();
}
None => {}
}
});
}) as Box<dyn FnMut(MessageEvent)>,
);
dc1.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
onmessage_callback.forget();