Closed emilk closed 1 year ago
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 process()
, presumably the function that you can't make non-blocking, making this basically an async iterator/stream.
@daxpedda unfortunately that yield doesn't work at all for me. It compiles, but my async task seems to get stuck.
Ah, apologies, my yield
example has a bug, it doesn't wake the Waker
.
Now that I think about it, it kinda depends on how the executor works.
I fixed it in my original post.
Will play around with it myself and see if it works.
Alright, this doesn't seem to work at all. I'm guessing the wasm-bindgen-futures
executor simply rechecks if something is ready again right away.
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.
Thanks @daxpedda, that at least clears things up a little bit for me!
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 a delay
of 0 can also do the job:
If this parameter is omitted, a value of 0 is used, meaning execute "immediately", or more accurately, the next event cycle.
Unfortunately nested timeouts will prevent this behavior and instead delay it by 4ms.
I discovered even more in the meantine:
Scheduler.postTask()
, only available in Chromium.Scheduler.yield()
, available nowhere.Both could do what you want and much more, really interesting developments.
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
requestAnimationFrame
). My code is something like this:I spawn this with
wasm_bindgen_futures::spawn_local
The problem is the
yield_
function. I've currently defined it as so:this feels very hacky, for obvious reasons.
Any suggestions for how to improve this?