-
I have a task where I need to decode a bunch of messages from a stream. Every 10ms I want to yield to my UI task (running on async fn decode_in_chunks(
mut decoder: Decoder,
process: Box<dyn Fn(Message)>,
) {
let mut last_yield = instant::Instant::now();
for msg in decoder {
process(msg);
if last_yield.elapsed() > instant::Duration::from_millis(10) {
// yield to the ui task
yield_().await;
last_yield = instant::Instant::now();
}
}
} I spawn this with The problem is the /// Yield to other tasks
async fn yield_() {
sleep_ms(1).await;
}
// Hack to get async sleep on wasm
async fn sleep_ms(millis: i32) {
let mut cb = |resolve: js_sys::Function, _reject: js_sys::Function| {
web_sys::window()
.unwrap()
.set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, millis)
.expect("Failed to call set_timeout");
};
let p = js_sys::Promise::new(&mut cb);
wasm_bindgen_futures::JsFuture::from(p).await.unwrap();
} this feels very hacky, for obvious reasons. Any suggestions for how to improve this? |
Beta Was this translation helpful? Give feedback.
Replies: 8 comments
-
Doesn't the usual Rust async yield work? struct Yield {
yielded: Option<bool>,
}
impl Yield {
fn new() -> Self {
Self {
yielded: Some(false),
}
}
}
impl Future for Yield {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let yielded = self.yielded.unwrap();
if yielded {
self.yielded = None;
Poll::Ready(())
} else {
self.yielded = Some(true);
cx.waker().wake_by_ref();
Poll::Pending
}
}
} Also it seems that the future you are spawning here is really blocking. Instead of putting a timer here I believe you should just yield in-between every call to |
Beta Was this translation helpful? Give feedback.
-
@daxpedda unfortunately that yield doesn't work at all for me. It compiles, but my async task seems to get stuck. |
Beta Was this translation helpful? Give feedback.
-
Ah, apologies, my Will play around with it myself and see if it works. |
Beta Was this translation helpful? Give feedback.
-
Alright, this doesn't seem to work at all. I'm guessing the |
Beta Was this translation helpful? Give feedback.
-
So after some testing basically my findings are that you can yield, but not how you want it. Basically you yield to the executor. So you can multiplex multiple futures by yielding, but you can't yield back to the "native" runtime because it's not part of the executor. Obviously web workers would be the perfect solution here, unfortunately the ecosystem around that is not exactly in a good state. I can't come up with a better solution then yours otherwise. |
Beta Was this translation helpful? Give feedback.
-
Thanks @daxpedda, that at least clears things up a little bit for me! |
Beta Was this translation helpful? Give feedback.
-
I actually found two viable solutions for this in the meantime:
|
Beta Was this translation helpful? Give feedback.
-
I discovered even more in the meantine:
Both could do what you want and much more, really interesting developments. |
Beta Was this translation helpful? Give feedback.
I actually found two viable solutions for this in the meantime:
window.requestIdleCallback()
which can be used to do exactly what you want here. Just let the future sleep and wake it up in the callback. Though this is unsupported in Safari (Bugzilla).While looking for Safari polyfills, I found that using
setTimeout()
with adelay
of 0 can also do the job:Unfortunately nested timeouts will prevent this behavior and instead delay it by 4ms.