js_sys
通过函数 Promise
公开 JavaScript pub fn new(cb: &mut dyn FnMut(Function, Function)) -> Promise;
。根据我对 MDN 文档的阅读,没有任何迹象表明执行器函数将被多次调用,但是 js_sys
将其公开为 FnMut
而不是 FnOnce
。
因此,我无法专注于简单的任务,例如将来通过频道向自己发送消息。理想情况下,通道的
sender
一半将移入封闭区域,并且 Promise::new
将接受 FnOnce
。
async func send_to_future_self() {
use std::sync::mpsc::{Sender, Receiver, channel};
let (sender, receiver) = channel().split();
// Schedule sending a message in the future
// Option A: Move `sender` --> The closure becomes `FnOnce` because it consumes `sender`
// Option B: Reference `sender` --> Then the borrow outlives `sender`'s lifetime
let mut sleep_100 = move?? |accept: Function, _reject: Function| {
// This Rust closure is exported to Javascript...
let callback = Closure::__??__(move?? || {
// Send a message via the channel
sender.send(()).unwrap();
let result = js_sys::Array::new();
// Doesn't matter what value is passed here, it's just for synchronization
result.push(&JsValue::from(true));
accept.apply(&JsValue::undefined(), &result).unwrap();
});
web_sys::window().unwrap()
.set_timeout_with_callback_and_timeout_and_arguments_0(
callback.as_ref().unchecked_ref(),
100
).unwrap();
// ... so intentionally forget it, otherwise it will be dropped at the end of this block
// despite JS still referring to it.
callback.forget();
};
// Convert JS Promise into a Rust future
// This wants an FnMut(Function, Function)
let future: JsFuture = js_sys::Promise::new(&mut sleep_100).into();
let _ = future.await;
receiver.try_recv().unwrap();
}
我怀疑它在内部被包装为 JavaScript 函数对象,可以多次调用(即使不会)。
解决此问题的最简单方法是让函数可多次调用。一种简单的方法是将消耗性值包装在
Option
中并使用 Option::take()
来获取该值 - 如果返回 None
则该函数被多次调用。
这里有一些示例代码说明了这种技术:
struct Consumable;
impl Consumable {
fn consume(self) {}
}
fn requires_fnmut(_: impl FnMut()) { }
fn main() {
let mut x = Some(Consumable);
requires_fnmut(move || {
let x = match x.take() {
Some(v) => v,
// If None we were called twice, so just bail.
None => return,
};
x.consume();
});
}
这里,
Consumable
与您的情况中的 Sender
类似:它公开了一个消耗接收器的方法。在闭包中的捕获变量上调用此函数将使闭包实现 FnOnce
而不是 FnMut
。
但是,请注意在
main()
中,我们将 Consumable
包裹在 Option
中并标记为 mut
:
let mut x = Some(Consumable);
现在我们可以在闭包中使用
match x.take()
来“解开”它,如果 take()
返回 None
,则从闭包返回,这表明该函数被第二次调用。
从
Option
获取价值后,您可以自由消费它。