应为"()",但找到异步函数的不透明类型



我遵循一个使用websys设置WebRTC数据通道的指南。我可以复制和粘贴代码,它可以正确编译。start((函数是异步的,这使得可以在主作用域内等待JsFuture,但我正试图将此等待移到onmessage_callback块。只需在最初的实现中添加这一行,我就可以得到:

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();

一旦我编译了这个,我当然会得到一个错误,说等待未来的desc只有在异步上下文中才可能。我想在定义FnMut函数时可以添加async关键字:

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();

但当我编译这个时,我得到了以下错误:

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)`

我不知道如何进行,我认为错误是说回调返回了一个未来,但它应该是无效的。

如何定义异步回调?

您通过说来声明您的函数不会返回任何内容

Box<dyn FnMut(MessageEvent)>
//which is the same thing as Box<dyn FnMut(MessageEvent)->()>

然后在回调中,实际上返回了一个异步块。

我不熟悉所讨论的库,但看看文档,你不应该传递异步函数。

如果必须使用async,并且运行时已经在运行,则可以在同步块中生成任务。

但在javascript世界中,如果某个东西是异步的,你就不能真正等待它——你只能给它一个回调(async/await关键字存在,但它们所做的只是给你另一个承诺(

因此,我认为您应该在这里使用promise API,并为then方法提供回调。

这并不漂亮,但这就是js世界中的事情,你在这里面临的被称为js中的回调地狱,这是一件事:D

Ps:Javascript有一个单线程运行时,事情会安排在一个永远运行的循环中,称为事件循环。每次在某个时刻将回调传递到某个位置时,它都会在事件循环中注册并最终被调用。

重要的是要理解,它的调用部分是不保证的,如果您在其中一个回调中阻止事件循环,那么循环的下一次滴答声将永远不会出现,因此整个运行时将锁定。这就是导致回调地狱的设计决策背后的原因

Pps:

我真的不认为你应该把一个rust-async运行时和你的wasm二进制文件捆绑在一起(如果可能的话(,因为js有它自己的,正如我提到的,只要坚持promise api

原因是@nikoss指定的,您传递一个未来返回函数,并将其强制转换为单元返回函数。

至于如何解决这个问题,您可以使用spawn_local():在JS promise微任务队列上生成未来

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();

最新更新