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(target_feature = "atomics", feature(stdarch_wasm_atomic_wait))]
34#![deny(missing_docs)]
35
36use js_sys::Promise;
37use std::cell::RefCell;
38use std::fmt;
39use std::future::Future;
40use std::pin::Pin;
41use std::rc::Rc;
42use std::task::{Context, Poll, Waker};
43use wasm_bindgen::prelude::*;
44
45mod queue;
46#[cfg(feature = "futures-core-03-stream")]
47pub mod stream;
48
49pub use js_sys;
50pub use wasm_bindgen;
51
52mod task {
53 use cfg_if::cfg_if;
54
55 cfg_if! {
56 if #[cfg(target_feature = "atomics")] {
57 mod wait_async_polyfill;
58 mod multithread;
59 pub(crate) use multithread::*;
60
61 } else {
62 mod singlethread;
63 pub(crate) use singlethread::*;
64 }
65 }
66}
67
68/// Runs a Rust `Future` on the current thread.
69///
70/// The `future` must be `'static` because it will be scheduled
71/// to run in the background and cannot contain any stack references.
72///
73/// The `future` will always be run on the next microtask tick even if it
74/// immediately returns `Poll::Ready`.
75///
76/// # Panics
77///
78/// This function has the same panic behavior as `future_to_promise`.
79#[inline]
80pub fn spawn_local<F>(future: F)
81where
82 F: Future<Output = ()> + 'static,
83{
84 task::Task::spawn(future:Box::pin(future));
85}
86
87struct Inner {
88 result: Option<Result<JsValue, JsValue>>,
89 task: Option<Waker>,
90 callbacks: Option<(Closure<dyn FnMut(JsValue)>, Closure<dyn FnMut(JsValue)>)>,
91}
92
93/// A Rust `Future` backed by a JavaScript `Promise`.
94///
95/// This type is constructed with a JavaScript `Promise` object and translates
96/// it to a Rust `Future`. This type implements the `Future` trait from the
97/// `futures` crate and will either succeed or fail depending on what happens
98/// with the JavaScript `Promise`.
99///
100/// Currently this type is constructed with `JsFuture::from`.
101pub struct JsFuture {
102 inner: Rc<RefCell<Inner>>,
103}
104
105impl fmt::Debug for JsFuture {
106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107 write!(f, "JsFuture {{ ... }}")
108 }
109}
110
111impl From<Promise> for JsFuture {
112 fn from(js: Promise) -> JsFuture {
113 // Use the `then` method to schedule two callbacks, one for the
114 // resolved value and one for the rejected value. We're currently
115 // assuming that JS engines will unconditionally invoke precisely one of
116 // these callbacks, no matter what.
117 //
118 // Ideally we'd have a way to cancel the callbacks getting invoked and
119 // free up state ourselves when this `JsFuture` is dropped. We don't
120 // have that, though, and one of the callbacks is likely always going to
121 // be invoked.
122 //
123 // As a result we need to make sure that no matter when the callbacks
124 // are invoked they are valid to be called at any time, which means they
125 // have to be self-contained. Through the `Closure::once` and some
126 // `Rc`-trickery we can arrange for both instances of `Closure`, and the
127 // `Rc`, to all be destroyed once the first one is called.
128 let state = Rc::new(RefCell::new(Inner {
129 result: None,
130 task: None,
131 callbacks: None,
132 }));
133
134 fn finish(state: &RefCell<Inner>, val: Result<JsValue, JsValue>) {
135 let task = {
136 let mut state = state.borrow_mut();
137 debug_assert!(state.callbacks.is_some());
138 debug_assert!(state.result.is_none());
139
140 // First up drop our closures as they'll never be invoked again and
141 // this is our chance to clean up their state.
142 drop(state.callbacks.take());
143
144 // Next, store the value into the internal state.
145 state.result = Some(val);
146 state.task.take()
147 };
148
149 // And then finally if any task was waiting on the value wake it up and
150 // let them know it's there.
151 if let Some(task) = task {
152 task.wake()
153 }
154 }
155
156 let resolve = {
157 let state = state.clone();
158 Closure::once(move |val| finish(&state, Ok(val)))
159 };
160
161 let reject = {
162 let state = state.clone();
163 Closure::once(move |val| finish(&state, Err(val)))
164 };
165
166 let _ = js.then2(&resolve, &reject);
167
168 state.borrow_mut().callbacks = Some((resolve, reject));
169
170 JsFuture { inner: state }
171 }
172}
173
174impl Future for JsFuture {
175 type Output = Result<JsValue, JsValue>;
176
177 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
178 let mut inner: RefMut<'_, Inner> = self.inner.borrow_mut();
179
180 // If our value has come in then we return it...
181 if let Some(val: Result) = inner.result.take() {
182 return Poll::Ready(val);
183 }
184
185 // ... otherwise we arrange ourselves to get woken up once the value
186 // does come in
187 inner.task = Some(cx.waker().clone());
188 Poll::Pending
189 }
190}
191
192/// Converts a Rust `Future` into a JavaScript `Promise`.
193///
194/// This function will take any future in Rust and schedule it to be executed,
195/// returning a JavaScript `Promise` which can then be passed to JavaScript.
196///
197/// The `future` must be `'static` because it will be scheduled to run in the
198/// background and cannot contain any stack references.
199///
200/// The returned `Promise` will be resolved or rejected when the future completes,
201/// depending on whether it finishes with `Ok` or `Err`.
202///
203/// # Panics
204///
205/// Note that in wasm panics are currently translated to aborts, but "abort" in
206/// this case means that a JavaScript exception is thrown. The wasm module is
207/// still usable (likely erroneously) after Rust panics.
208///
209/// If the `future` provided panics then the returned `Promise` **will not
210/// resolve**. Instead it will be a leaked promise. This is an unfortunate
211/// limitation of wasm currently that's hoped to be fixed one day!
212pub fn future_to_promise<F>(future: F) -> Promise
213where
214 F: Future<Output = Result<JsValue, JsValue>> + 'static,
215{
216 let mut future: Option = Some(future);
217
218 Promise::new(&mut |resolve, reject| {
219 let future: F = future.take().unwrap_throw();
220
221 spawn_local(future:async move {
222 match future.await {
223 Ok(val: JsValue) => {
224 resolve.call1(&JsValue::undefined(), &val).unwrap_throw();
225 }
226 Err(val: JsValue) => {
227 reject.call1(&JsValue::undefined(), &val).unwrap_throw();
228 }
229 }
230 });
231 })
232}
233