Skip to main content

wasm_bindgen_futures/
lib.rs

1//! Converting between JavaScript `Promise`s to Rust `Future`s.
2//!
3//! This crate provides a bridge for working with JavaScript `Promise` types as
4//! a Rust `Future`, and similarly contains utilities to turn a rust `Future`
5//! into a JavaScript `Promise`. This can be useful when working with
6//! asynchronous or otherwise blocking work in Rust (wasm), and provides the
7//! ability to interoperate with JavaScript events and JavaScript I/O
8//! primitives.
9//!
10//! There are three main interfaces in this crate currently:
11//!
12//! 1. [**`JsFuture`**](./struct.JsFuture.html)
13//!
14//!    A type that is constructed with a `Promise` and can then be used as a
15//!    `Future<Output = Result<JsValue, JsValue>>`. This Rust future will resolve
16//!    or reject with the value coming out of the `Promise`.
17//!
18//! 2. [**`future_to_promise`**](./fn.future_to_promise.html)
19//!
20//!    Converts a Rust `Future<Output = Result<JsValue, JsValue>>` into a
21//!    JavaScript `Promise`. The future's result will translate to either a
22//!    resolved or rejected `Promise` in JavaScript.
23//!
24//! 3. [**`spawn_local`**](./fn.spawn_local.html)
25//!
26//!    Spawns a `Future<Output = ()>` on the current thread. This is the
27//!    best way to run a `Future` in Rust without sending it to JavaScript.
28//!
29//! These three items should provide enough of a bridge to interoperate the two
30//! systems and make sure that Rust/JavaScript can work together with
31//! asynchronous and I/O work.
32
33#![cfg_attr(not(feature = "std"), no_std)]
34#![cfg_attr(
35    target_feature = "atomics",
36    feature(thread_local, stdarch_wasm_atomic_wait)
37)]
38#![deny(missing_docs)]
39#![cfg_attr(docsrs, feature(doc_cfg))]
40
41extern crate alloc;
42
43use alloc::rc::Rc;
44use core::cell::RefCell;
45use core::fmt;
46use core::future::Future;
47use core::panic::AssertUnwindSafe;
48use core::pin::Pin;
49use core::task::{Context, Poll, Waker};
50#[cfg(all(target_arch = "wasm32", feature = "std", panic = "unwind"))]
51use futures_util::FutureExt;
52use js_sys::Promise;
53use wasm_bindgen::__rt::marker::ErasableGeneric;
54#[cfg(all(target_arch = "wasm32", feature = "std", panic = "unwind"))]
55use wasm_bindgen::__rt::panic_to_panic_error;
56use wasm_bindgen::convert::{FromWasmAbi, Upcast};
57use wasm_bindgen::{prelude::*, JsError, JsGeneric};
58
59mod queue;
60#[cfg_attr(docsrs, doc(cfg(feature = "futures-core-03-stream")))]
61#[cfg(feature = "futures-core-03-stream")]
62pub mod stream;
63
64pub use js_sys;
65pub use wasm_bindgen;
66
67mod task {
68    use cfg_if::cfg_if;
69
70    cfg_if! {
71        if #[cfg(target_feature = "atomics")] {
72            mod wait_async_polyfill;
73            mod multithread;
74            pub(crate) use multithread::*;
75
76        } else {
77            mod singlethread;
78            pub(crate) use singlethread::*;
79         }
80    }
81}
82
83/// Runs a Rust `Future` on the current thread.
84///
85/// The `future` must be `'static` because it will be scheduled
86/// to run in the background and cannot contain any stack references.
87///
88/// The `future` will always be run on the next microtask tick even if it
89/// immediately returns `Poll::Ready`.
90///
91/// # Panics
92///
93/// This function has the same panic behavior as `future_to_promise`.
94#[inline]
95pub fn spawn_local<F>(future: F)
96where
97    F: Future<Output = ()> + 'static,
98{
99    task::Task::spawn(future);
100}
101
102struct Inner<T = JsValue> {
103    result: Option<Result<T, JsValue>>,
104    task: Option<Waker>,
105    callbacks: Option<(
106        Closure<dyn FnMut(T) -> Result<(), JsError>>,
107        Closure<dyn FnMut(JsValue) -> Result<(), JsError>>,
108    )>,
109}
110
111/// A Rust `Future` backed by a JavaScript `Promise`.
112///
113/// This type is constructed with a JavaScript `Promise` object and translates
114/// it to a Rust `Future`. This type implements the `Future` trait from the
115/// `futures` crate and will either succeed or fail depending on what happens
116/// with the JavaScript `Promise`.
117///
118/// Currently this type is constructed with `JsFuture::from`.
119pub struct JsFuture<T = JsValue> {
120    inner: Rc<RefCell<Inner<T>>>,
121}
122
123impl core::panic::UnwindSafe for JsFuture {}
124
125unsafe impl<T> ErasableGeneric for JsFuture<T> {
126    type Repr = JsFuture<JsValue>;
127}
128
129// Upcast for JsFuture is covariant in T (the success type)
130// JsFuture<T> can upcast to JsFuture<Target> if T: Upcast<Target>
131impl<T, Target> Upcast<JsFuture<Target>> for JsFuture<T> where T: Upcast<Target> {}
132
133impl<T> fmt::Debug for JsFuture<T> {
134    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135        write!(f, "JsFuture {{ ... }}")
136    }
137}
138
139impl<T: 'static + FromWasmAbi> From<Promise<T>> for JsFuture<T> {
140    fn from(js: Promise<T>) -> JsFuture<T> {
141        // Use the `then` method to schedule two callbacks, one for the
142        // resolved value and one for the rejected value. We're currently
143        // assuming that JS engines will unconditionally invoke precisely one of
144        // these callbacks, no matter what.
145        //
146        // Ideally we'd have a way to cancel the callbacks getting invoked and
147        // free up state ourselves when this `JsFuture` is dropped. We don't
148        // have that, though, and one of the callbacks is likely always going to
149        // be invoked.
150        //
151        // As a result we need to make sure that no matter when the callbacks
152        // are invoked they are valid to be called at any time, which means they
153        // have to be self-contained. Through the `Closure::once` and some
154        // `Rc`-trickery we can arrange for both instances of `Closure`, and the
155        // `Rc`, to all be destroyed once the first one is called.
156        let state = Rc::new(RefCell::new(Inner::<T> {
157            result: None,
158            task: None,
159            callbacks: None,
160        }));
161
162        fn finish<T>(state: &RefCell<Inner<T>>, val: Result<T, JsValue>) {
163            let task = {
164                let mut state = state.borrow_mut();
165                assert!(
166                    state.callbacks.is_some(),
167                    "finish: callbacks should be Some"
168                );
169                assert!(state.result.is_none(), "finish: result should be None");
170
171                // First up drop our closures as they'll never be invoked again and
172                // this is our chance to clean up their state.
173                drop(state.callbacks.take());
174
175                // Next, store the value into the internal state.
176                state.result = Some(val);
177                state.task.take()
178            };
179
180            // And then finally if any task was waiting on the value wake it up and
181            // let them know it's there.
182            if let Some(task) = task {
183                task.wake()
184            }
185        }
186
187        let resolve = {
188            let state = AssertUnwindSafe(state.clone());
189            Closure::once(move |val: T| {
190                finish(&*state, Ok(val));
191                Ok(())
192            })
193        };
194
195        let reject = {
196            let state = AssertUnwindSafe(state.clone());
197            Closure::once(move |val| {
198                finish(&*state, Err(val));
199                Ok(())
200            })
201        };
202
203        let _ = js.then_with_reject(&resolve, &reject);
204
205        state.borrow_mut().callbacks = Some((resolve, reject));
206
207        JsFuture { inner: state }
208    }
209}
210
211impl<T> Future for JsFuture<T> {
212    type Output = Result<T, JsValue>;
213
214    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
215        let mut inner = self.inner.borrow_mut();
216
217        // If our value has come in then we return it...
218        if let Some(val) = inner.result.take() {
219            return Poll::Ready(val);
220        }
221
222        // ... otherwise we arrange ourselves to get woken up once the value
223        // does come in
224        inner.task = Some(cx.waker().clone());
225        Poll::Pending
226    }
227}
228
229/// Converts a Rust `Future` into a JavaScript `Promise`.
230///
231/// This function will take any future in Rust and schedule it to be executed,
232/// returning a JavaScript `Promise` which can then be passed to JavaScript.
233///
234/// The `future` must be `'static` because it will be scheduled to run in the
235/// background and cannot contain any stack references.
236///
237/// The returned `Promise` will be resolved or rejected when the future
238/// completes, depending on whether it finishes with `Ok` or `Err`.
239///
240/// # Panics
241///
242/// Note that in Wasm panics are currently translated to aborts, but "abort" in
243/// this case means that a JavaScript exception is thrown. The Wasm module is
244/// still usable (likely erroneously) after Rust panics.
245#[cfg(not(all(target_arch = "wasm32", feature = "std", panic = "unwind")))]
246pub fn future_to_promise<F>(future: F) -> Promise
247where
248    F: Future<Output = Result<JsValue, JsValue>> + 'static,
249{
250    let mut future = Some(future);
251
252    Promise::new_typed(&mut move |resolve, reject| {
253        let future = future.take().unwrap_throw();
254
255        spawn_local(async move {
256            match future.await {
257                Ok(val) => {
258                    resolve.call(&JsValue::undefined(), (&val,)).unwrap_throw();
259                }
260                Err(val) => {
261                    reject.call(&JsValue::undefined(), (&val,)).unwrap_throw();
262                }
263            }
264        });
265    })
266}
267
268/// Converts a Rust `Future` into a JavaScript `Promise`.
269///
270/// This function will take any future in Rust and schedule it to be executed,
271/// returning a JavaScript `Promise` which can then be passed to JavaScript.
272///
273/// The `future` must be `'static` because it will be scheduled to run in the
274/// background and cannot contain any stack references.
275///
276/// The returned `Promise` will be resolved or rejected when the future
277/// completes, depending on whether it finishes with `Ok` or `Err`.
278///
279/// # Panics
280///
281/// If the `future` provided panics then the returned `Promise` will be rejected
282/// with a PanicError.
283#[cfg(all(target_arch = "wasm32", feature = "std", panic = "unwind"))]
284pub fn future_to_promise<F>(future: F) -> Promise
285where
286    F: Future<Output = Result<JsValue, JsValue>> + 'static + std::panic::UnwindSafe,
287{
288    let mut future = Some(future);
289    Promise::new(&mut |resolve, reject| {
290        let future = future.take().unwrap_throw();
291        spawn_local(async move {
292            let res = future.catch_unwind().await;
293            match res {
294                Ok(Ok(val)) => {
295                    resolve.call(&JsValue::undefined(), (&val,)).unwrap_throw();
296                }
297                Ok(Err(val)) => {
298                    reject.call(&JsValue::undefined(), (&val,)).unwrap_throw();
299                }
300                Err(val) => {
301                    reject
302                        .call(&JsValue::undefined(), (&panic_to_panic_error(val),))
303                        .unwrap_throw();
304                }
305            }
306        });
307    })
308}
309
310// Note: Once we bump MSRV, we can type future_to_promise with backwards compatible inference.
311/// Converts a Rust `Future` into a corresponding typed JavaScript `Promise<T>`.
312///
313/// This function will take any future in Rust and schedule it to be executed,
314/// returning a JavaScript `Promise` which can then be passed to JavaScript.
315///
316/// The `future` must be `'static` because it will be scheduled to run in the
317/// background and cannot contain any stack references.
318///
319/// The returned `Promise` will be resolved or rejected when the future completes,
320/// depending on whether it finishes with `Ok` or `Err`.
321///
322/// # Panics
323///
324/// Note that in Wasm panics are currently translated to aborts, but "abort" in
325/// this case means that a JavaScript exception is thrown. The Wasm module is
326/// still usable (likely erroneously) after Rust panics.
327///
328/// If the `future` provided panics then the returned `Promise` **will not
329/// resolve**. Instead it will be a leaked promise. This is an unfortunate
330/// limitation of Wasm currently that's hoped to be fixed one day!
331pub fn future_to_promise_typed<F, T>(future: F) -> Promise<T>
332where
333    F: Future<Output = Result<T, JsValue>> + 'static,
334    T: FromWasmAbi + JsGeneric + Upcast<T> + 'static,
335{
336    let mut future = Some(future);
337
338    Promise::new_typed(&mut move |resolve, reject| {
339        let future = future.take().unwrap_throw();
340        spawn_local(async move {
341            match future.await {
342                Ok(val) => {
343                    resolve.call(&JsValue::undefined(), (&val,)).unwrap_throw();
344                }
345                Err(val) => {
346                    reject.call(&JsValue::undefined(), (&val,)).unwrap_throw();
347                }
348            }
349        });
350    })
351}