Skip to main content

js_sys/futures/
mod.rs

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