Skip to content

Commit d41202a

Browse files
authored
wasm-bindgen-futures: use queueMicrotask for next tick runs (#3203) (#3611)
1 parent 8d063a4 commit d41202a

File tree

2 files changed

+31
-4
lines changed

2 files changed

+31
-4
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@
9292
`#[repr(C)]` types.
9393
[#3595](https://github.com/rustwasm/wasm-bindgen/pull/3595)
9494

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

97103
* Fixed bindings and comments for `Atomics.wait`.

crates/futures/src/queue.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ use std::collections::VecDeque;
44
use std::rc::Rc;
55
use wasm_bindgen::prelude::*;
66

7+
#[wasm_bindgen]
8+
extern "C" {
9+
#[wasm_bindgen]
10+
fn queueMicrotask(closure: &Closure<dyn FnMut(JsValue)>);
11+
12+
type Global;
13+
14+
#[wasm_bindgen(method, getter, js_name = queueMicrotask)]
15+
fn hasQueueMicrotask(this: &Global) -> JsValue;
16+
}
17+
718
struct QueueState {
819
// The queue of Tasks which are to be run in order. In practice this is all the
920
// synchronous work of futures, and each `Task` represents calling `poll` on
@@ -42,17 +53,21 @@ pub(crate) struct Queue {
4253
state: Rc<QueueState>,
4354
promise: Promise,
4455
closure: Closure<dyn FnMut(JsValue)>,
56+
has_queue_microtask: bool,
4557
}
4658

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

88+
let has_queue_microtask = js_sys::global()
89+
.unchecked_into::<Global>()
90+
.hasQueueMicrotask()
91+
.is_function();
92+
7393
Self {
7494
promise: Promise::resolve(&JsValue::undefined()),
7595

@@ -82,6 +102,7 @@ impl Queue {
82102
},
83103

84104
state,
105+
has_queue_microtask,
85106
}
86107
}
87108
}

0 commit comments

Comments
 (0)