diff --git a/crates/futures/src/legacy.rs b/crates/futures/src/legacy.rs index 18fcde41..a200d088 100644 --- a/crates/futures/src/legacy.rs +++ b/crates/futures/src/legacy.rs @@ -1,96 +1,11 @@ +use futures::executor::{self, Notify, Spawn}; +use futures::prelude::*; +use js_sys::{Function, Promise}; use std::cell::{Cell, RefCell}; -use std::fmt; use std::rc::Rc; use std::sync::Arc; - -use futures::executor::{self, Notify, Spawn}; -use futures::future; -use futures::prelude::*; -use futures::sync::oneshot; -use js_sys::{Function, Promise}; use wasm_bindgen::prelude::*; -/// A Rust `Future` backed by a JavaScript `Promise`. -/// -/// This type is constructed with a JavaScript `Promise` object and translates -/// it to a Rust `Future`. This type implements the `Future` trait from the -/// `futures` crate and will either succeed or fail depending on what happens -/// with the JavaScript `Promise`. -/// -/// Currently this type is constructed with `JsFuture::from`. -pub struct JsFuture { - rx: oneshot::Receiver>, -} - -impl fmt::Debug for JsFuture { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "JsFuture {{ ... }}") - } -} - -impl From for JsFuture { - fn from(js: Promise) -> JsFuture { - // Use the `then` method to schedule two callbacks, one for the - // resolved value and one for the rejected value. We're currently - // assuming that JS engines will unconditionally invoke precisely one of - // these callbacks, no matter what. - // - // Ideally we'd have a way to cancel the callbacks getting invoked and - // free up state ourselves when this `JsFuture` is dropped. We don't - // have that, though, and one of the callbacks is likely always going to - // be invoked. - // - // As a result we need to make sure that no matter when the callbacks - // are invoked they are valid to be called at any time, which means they - // have to be self-contained. Through the `Closure::once` and some - // `Rc`-trickery we can arrange for both instances of `Closure`, and the - // `Rc`, to all be destroyed once the first one is called. - let (tx, rx) = oneshot::channel(); - let state = Rc::new(RefCell::new(None)); - let state2 = state.clone(); - let resolve = Closure::once(move |val| finish(&state2, Ok(val))); - let state2 = state.clone(); - let reject = Closure::once(move |val| finish(&state2, Err(val))); - - js.then2(&resolve, &reject); - *state.borrow_mut() = Some((tx, resolve, reject)); - - return JsFuture { rx }; - - fn finish( - state: &RefCell< - Option<( - oneshot::Sender>, - Closure, - Closure, - )>, - >, - val: Result, - ) { - match state.borrow_mut().take() { - // We don't have any guarantee that anyone's still listening at this - // point (the Rust `JsFuture` could have been dropped) so simply - // ignore any errors here. - Some((tx, _, _)) => drop(tx.send(val)), - None => wasm_bindgen::throw_str("cannot finish twice"), - } - } - } -} - -impl Future for JsFuture { - type Item = JsValue; - type Error = JsValue; - - fn poll(&mut self) -> Poll { - match self.rx.poll() { - Ok(Async::Ready(val)) => val.map(Async::Ready), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(_) => wasm_bindgen::throw_str("cannot cancel"), - } - } -} - /// Converts a Rust `Future` into a JavaScript `Promise`. /// /// This function will take any future in Rust and schedule it to be executed, @@ -112,8 +27,8 @@ impl Future for JsFuture { /// resolve**. Instead it will be a leaked promise. This is an unfortunate /// limitation of wasm currently that's hoped to be fixed one day! pub fn future_to_promise(future: F) -> Promise - where - F: Future + 'static, +where + F: Future + 'static, { _future_to_promise(Box::new(future)) } @@ -287,22 +202,3 @@ fn _future_to_promise(future: Box>) } } } - -/// Converts a Rust `Future` on a local task queue. -/// -/// The `future` provided must adhere to `'static` because it'll be scheduled -/// to run in the background and cannot contain any stack references. -/// -/// # Panics -/// -/// This function has the same panic behavior as `future_to_promise`. -pub fn spawn_local(future: F) - where - F: Future + 'static, -{ - future_to_promise( - future - .map(|()| JsValue::undefined()) - .or_else(|()| future::ok::(JsValue::undefined())), - ); -} diff --git a/crates/futures/src/legacy_atomics.rs b/crates/futures/src/legacy_atomics.rs index 9981b695..b09e8b7c 100644 --- a/crates/futures/src/legacy_atomics.rs +++ b/crates/futures/src/legacy_atomics.rs @@ -1,10 +1,8 @@ -use std::sync::atomic::{AtomicI32, Ordering}; -use std::sync::Arc; - use futures::executor::{self, Notify, Spawn}; -use futures::future; use futures::prelude::*; use js_sys::Function; +use std::sync::atomic::{AtomicI32, Ordering}; +use std::sync::Arc; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; @@ -165,24 +163,4 @@ fn wait_async(ptr: &AtomicI32, val: i32) -> js_sys::Promise { let mem = wasm_bindgen::memory().unchecked_into::(); Atomics::wait_async(&mem.buffer(), ptr as *const AtomicI32 as i32 / 4, val) } - -} - -/// Converts a Rust `Future` on a local task queue. -/// -/// The `future` provided must adhere to `'static` because it'll be scheduled -/// to run in the background and cannot contain any stack references. -/// -/// # Panics -/// -/// This function has the same panic behavior as `future_to_promise`. -pub fn spawn_local(future: F) -where - F: Future + 'static, -{ - future_to_promise( - future - .map(|()| JsValue::undefined()) - .or_else(|()| future::ok::(JsValue::undefined())), - ); } diff --git a/crates/futures/src/legacy_js2rust.rs b/crates/futures/src/legacy_shared.rs similarity index 85% rename from crates/futures/src/legacy_js2rust.rs rename to crates/futures/src/legacy_shared.rs index a4fb52c4..a53e32e1 100644 --- a/crates/futures/src/legacy_js2rust.rs +++ b/crates/futures/src/legacy_shared.rs @@ -1,7 +1,7 @@ use std::cell::RefCell; +use futures::future; use std::fmt; use std::rc::Rc; - use futures::prelude::*; use futures::sync::oneshot; use js_sys::Promise; @@ -87,3 +87,22 @@ impl Future for JsFuture { } } } + +/// Converts a Rust `Future` on a local task queue. +/// +/// The `future` provided must adhere to `'static` because it'll be scheduled +/// to run in the background and cannot contain any stack references. +/// +/// # Panics +/// +/// This function has the same panic behavior as `future_to_promise`. +pub fn spawn_local(future: F) +where + F: Future + 'static, +{ + crate::future_to_promise( + future + .map(|()| JsValue::undefined()) + .or_else(|()| future::ok::(JsValue::undefined())), + ); +} diff --git a/crates/futures/src/lib.rs b/crates/futures/src/lib.rs index ed7aec06..57da7b36 100644 --- a/crates/futures/src/lib.rs +++ b/crates/futures/src/lib.rs @@ -106,18 +106,17 @@ use cfg_if::cfg_if; -mod legacy_js2rust; -pub use legacy_js2rust::*; +mod legacy_shared; +pub use legacy_shared::*; cfg_if! { if #[cfg(target_feature = "atomics")] { /// Contains a thread-safe version of this crate, with Futures 0.1 mod legacy_atomics; + pub use legacy_atomics::*; /// Polyfill for `Atomics.waitAsync` function mod wait_async_polyfill; - - pub use legacy_atomics::*; } else { mod legacy; pub use legacy::*;