1

我正在遵循使用 web-sys 设置 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只能在异步上下文中。我想我可以在定义函数async时添加关键字:FnMut

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

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

如何定义异步回调?

4

2 回答 2

2

你声明你的函数不会返回任何东西

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

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

我不熟悉有问题的库,但查看你不应该传递异步函数的文档。

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

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

所以我相信你应该在这里使用promise API 并为该then方法提供回调。

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

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

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

pps:

我真的不认为你应该将 rust 异步运行时与你的 wasm 二进制文件捆绑在一起(如果可能的话),因为 js 有它自己的,正如我提到的,只要坚持使用 promise api

于 2022-01-23T19:57:52.907 回答
1

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

至于如何解决这个问题,您可以在 JS 承诺微任务队列上生成未来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();
于 2022-01-23T20:44:01.337 回答