Skip to content

Commit

Permalink
wasm-bindgen-futures: use queueMicrotask for next tick runs (#3203)
Browse files Browse the repository at this point in the history
but use the old `Promise.then` mechanism as a fallback. Cache the
existance of `queueMicrotask` in the queue instance.

Signed-off-by: Dominik Csapak <[email protected]>
  • Loading branch information
flumm committed Sep 15, 2023
1 parent 8d063a4 commit 44126a3
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 4 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@
`#[repr(C)]` types.
[#3595](https://github.com/rustwasm/wasm-bindgen/pull/3595)

* Use `queueMicrotask` in `wasm-bindgen-futures` for scheduling tasks on the next tick.
If that is not available, use the previous `Promise.then` mechanism as a fallback.
This should avoid quirks, like exceptions thrown get now properly reported
as normal exceptions rather than as rejected promises.
[#3611](https://github.com/rustwasm/wasm-bindgen/pull/3611)
[#2392](https://github.com/rustwasm/wasm-bindgen/issues/2392)

### Fixed

* Fixed bindings and comments for `Atomics.wait`.
Expand Down
31 changes: 27 additions & 4 deletions crates/futures/src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ use std::collections::VecDeque;
use std::rc::Rc;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen]
fn queueMicrotask(closure: &Closure<dyn FnMut(JsValue)>);
}

#[wasm_bindgen]
extern "C" {
type Global;
#[wasm_bindgen(method, getter, js_name = queueMicrotask)]
fn hasQueueMicrotask(this: &Global) -> JsValue;
}

struct QueueState {
// The queue of Tasks which are to be run in order. In practice this is all the
// synchronous work of futures, and each `Task` represents calling `poll` on
Expand Down Expand Up @@ -42,17 +55,21 @@ pub(crate) struct Queue {
state: Rc<QueueState>,
promise: Promise,
closure: Closure<dyn FnMut(JsValue)>,
has_queue_microtask: bool,
}

impl Queue {
// Schedule a task to run on the next tick
pub(crate) fn schedule_task(&self, task: Rc<crate::task::Task>) {
self.state.tasks.borrow_mut().push_back(task);
// Note that we currently use a promise and a closure to do this, but
// eventually we should probably use something like `queueMicrotask`:
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/queueMicrotask
// Use queueMicrotask to execute as soon as possible. If it does not exist
// fall back to the promise resolution
if !self.state.is_scheduled.replace(true) {
let _ = self.promise.then(&self.closure);
if self.has_queue_microtask {
queueMicrotask(&self.closure);
} else {
let _ = self.promise.then(&self.closure);
}
}
}
// Append a task to the currently running queue, or schedule it
Expand All @@ -70,6 +87,11 @@ impl Queue {
tasks: RefCell::new(VecDeque::new()),
});

let has_queue_microtask = js_sys::global()
.unchecked_into::<Global>()
.hasQueueMicrotask()
.is_function();

Self {
promise: Promise::resolve(&JsValue::undefined()),

Expand All @@ -82,6 +104,7 @@ impl Queue {
},

state,
has_queue_microtask,
}
}
}
Expand Down

0 comments on commit 44126a3

Please sign in to comment.