From 170f20e1fd7269a7daba96c93003a05e7a6abe4a Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Wed, 8 Aug 2018 17:41:36 -0700 Subject: [PATCH 1/4] futures: Add more documentation and example usage Adds an example future that becomes ready on the next tick of the JavaScript micro task queue. Part of #614 --- crates/futures/README.md | 17 ++--- crates/futures/src/lib.rs | 142 ++++++++++++++++++++++++++++++-------- 2 files changed, 122 insertions(+), 37 deletions(-) diff --git a/crates/futures/README.md b/crates/futures/README.md index 7e90bf29..9f6518fe 100644 --- a/crates/futures/README.md +++ b/crates/futures/README.md @@ -1,12 +1,13 @@ -# wasm-bindgen-futures +# `wasm-bindgen-futures` -[Documention][documentation] +[API Documention][docs] -This is an experimental crate (aka just written) which is targeted at bridging -a Rust `Future` and a JS `Promise`. Internally it contains two conversions, one -from a JS `Promise` to a Rust `Future`, and another from a Rust `Future` to a -JS `Promise`. +This crate bridges the gap between a Rust `Future` and a JavaScript +`Promise`. It provides two conversions: -See the [documentation] for more info. +1. From a JavaScript `Promise` into a Rust `Future`. +2. From a Rust `Future` into a JavaScript `Promise`. -[documentation]: https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/ +See the [API documentation][docs] for more info. + +[docs]: https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/ diff --git a/crates/futures/src/lib.rs b/crates/futures/src/lib.rs index 60ab9ef5..992bc4e1 100644 --- a/crates/futures/src/lib.rs +++ b/crates/futures/src/lib.rs @@ -1,23 +1,107 @@ -//! A JS `Promise` to Rust `Future` bridge +//! Converting between JavaScript `Promise`s to Rust `Future`s. //! -//! This crate provides a bridge for working with JS `Promise` types as a Rust -//! `Future`, and similarly contains utilities to turn a rust `Future` into a JS -//! `Promise`. This can be useful when working with asynchronous or otherwise -//! blocking work in Rust (wasm), and provides the ability to interoperate with -//! JS events and JS I/O primitives. +//! This crate provides a bridge for working with JavaScript `Promise` types as +//! a Rust `Future`, and similarly contains utilities to turn a rust `Future` +//! into a JavaScript `Promise`. This can be useful when working with +//! asynchronous or otherwise blocking work in Rust (wasm), and provides the +//! ability to interoperate with JavaScript events and JavaScript I/O +//! primitives. //! //! There are two main interfaces in this crate currently: //! -//! * `JsFuture` - a type that is constructed with a `Promise` and can then be -//! used as a `Future`. This Rust future will -//! resolve or reject with the value coming out of the `Promise`. -//! * `future_to_promise` - converts a Rust `Future` into a JS `Promise`. The future's result will translate to -//! either a rejected or resolved `Promise` in JS. +//! 1. [**`JsFuture`**](./struct.JsFuture.html) //! -//! These two types should provide enough of a bridge to interoperate the two -//! systems and make sure that Rust/JS can work together with asynchronous and -//! I/O work. +//! A type that is constructed with a `Promise` and can then be used as a +//! `Future`. This Rust future will resolve +//! or reject with the value coming out of the `Promise`. +//! +//! 2. [**`future_to_promise`**](./fn.future_to_promise.html) +//! +//! Converts a Rust `Future` into a +//! JavaScript `Promise`. The future's result will translate to either a +//! rejected or resolved `Promise` in JavaScript. +//! +//! These two items should provide enough of a bridge to interoperate the two +//! systems and make sure that Rust/JavaScript can work together with +//! asynchronous and I/O work. +//! +//! # Example Usage +//! +//! This example wraps JavaScript's `Promise.resolve()` into a Rust `Future` for +//! running tasks on the next tick of the micro task queue. The futures built on +//! top of it can be scheduled for execution by conversion into a JavaScript +//! `Promise`. +//! +//! ```rust,no_run +//! #![feature(use_extern_macros)] +//! +//! extern crate futures; +//! extern crate js_sys; +//! extern crate wasm_bindgen; +//! extern crate wasm_bindgen_futures; +//! +//! use futures::{Async, Future, Poll}; +//! use wasm_bindgen::prelude::*; +//! use wasm_bindgen_futures::{JsFuture, future_to_promise}; +//! +//! /// A future that becomes ready after a tick of the micro task queue. +//! pub struct NextTick { +//! inner: JsFuture, +//! } +//! +//! impl NextTick { +//! /// Construct a new `NextTick` future. +//! pub fn new() -> NextTick { +//! // Create a resolved promise that will run its callbacks on the next +//! // tick of the micro task queue. +//! let promise = js_sys::Promise::resolve(JsValue::NULL); +//! // Convert the promise into a `JsFuture`. +//! let inner = JsFuture::from(promise); +//! NextTick { inner } +//! } +//! } +//! +//! impl Future for NextTick { +//! type Item = (); +//! type Error = (); +//! +//! fn poll(&mut self) -> Poll<(), ()> { +//! // Polling a `NextTick` just forwards to polling if the inner promise is +//! // ready. +//! match self.inner.poll() { +//! Ok(Async::Ready(_)) => Ok(Async::Ready(())), +//! Ok(Async::NotReady) => Ok(Async::NotReady), +//! Err(_) => unreachable!( +//! "We only create NextTick with a resolved inner promise, never \ +//! a rejected one, so we can't get an error here" +//! ), +//! } +//! } +//! } +//! +//! /// Export a function to JavaScript that does some work in the next tick of the +//! /// micro task queue! +//! #[wasm_bindgen] +//! pub fn schedule_some_work_for_next_tick() -> js_sys::Promise { +//! let future = NextTick::new() +//! // Do some work... +//! .and_then(|_| { +//! Ok(42) +//! }) +//! // And then convert the `Item` and `Error` into `JsValue`. +//! .map(|result| { +//! JsValue::from(result) +//! }) +//! .map_err(|error| { +//! let js_error = js_sys::Error::new(&format!("uh oh! {:?}", error)); +//! JsValue::from(js_error) +//! }); +//! +//! // Convert the `Future` into a JavaScript +//! // `Promise`! +//! future_to_promise(future) +//! } +//! ``` #![deny(missing_docs)] #![feature(use_extern_macros)] @@ -35,12 +119,12 @@ use futures::sync::oneshot; use js_sys::{Function, Promise}; use wasm_bindgen::prelude::*; -/// A Rust `Future` backed by a JS `Promise`. +/// A Rust `Future` backed by a JavaScript `Promise`. /// -/// This type is constructed with a JS `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 JS -/// `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 { @@ -99,11 +183,11 @@ impl Future for JsFuture { } } -/// Converts a Rust `Future` into a JS `Promise`. +/// Converts a Rust `Future` into a JavaScript `Promise`. /// /// This function will take any future in Rust and schedule it to be executed, -/// returning a JS `Promise` which can then be passed back to JS to get plumbed -/// into the rest of a system. +/// returning a JavaScript `Promise` which can then be passed back to JavaScript +/// to get plumbed into the rest of a system. /// /// The `future` provided must adhere to `'static` because it'll be scheduled /// to run in the background and cannot contain any stack references. The @@ -113,8 +197,8 @@ impl Future for JsFuture { /// # Panics /// /// Note that in wasm panics are currently translated to aborts, but "abort" in -/// this case means that a JS exception is thrown. The wasm module is still -/// usable (likely erroneously) after Rust panics. +/// this case means that a JavaScript exception is thrown. The wasm module is +/// still usable (likely erroneously) after Rust panics. /// /// If the `future` provided panics then the returned `Promise` **will not /// resolve**. Instead it will be a leaked promise. This is an unfortunate @@ -125,14 +209,14 @@ pub fn future_to_promise(future: F) -> Promise _future_to_promise(Box::new(future)) } -// Implementation of actually transforming a future into a JS `Promise`. +// Implementation of actually transforming a future into a JavaScript `Promise`. // // The only primitive we have to work with here is `Promise::new`, which gives // us two callbacks that we can use to either reject or resolve the promise. // It's our job to ensure that one of those callbacks is called at the // appropriate time. // -// Now we know that JS (in general) can't block and is largely +// Now we know that JavaScript (in general) can't block and is largely // notification/callback driven. That means that our future must either have // synchronous computational work to do, or it's "scheduled a notification" to // happen. These notifications are likely callbacks to get executed when things @@ -164,8 +248,8 @@ fn _future_to_promise(future: Box>) -> P // notification to come in (and no one is polling). notified: Cell, - // Our two callbacks connected to the `Promise` that we returned to JS. - // We'll be invoking one of these at the end. + // Our two callbacks connected to the `Promise` that we returned to + // JavaScript. We'll be invoking one of these at the end. resolve: Function, reject: Function, } From 05dd67a63f741e2d250e13a6ac8ea02d894a41f0 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Wed, 8 Aug 2018 17:43:54 -0700 Subject: [PATCH 2/4] ci: Also test `wasm-bindgen-futures` --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index a8c1c002..2721f4c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,6 +62,9 @@ matrix: - cargo test --target wasm32-unknown-unknown --features serde-serialize # Make sure the `std` feature works if disabled - cargo test --target wasm32-unknown-unknown -p no-std + # Make sure the `wasm-bindgen-futures` tests pass. Right now, this just + # verifies that the example program in the crate level docs compiles. + - cargo test -p wasm-bindgen-futures addons: firefox: latest if: branch = master From 42e02f7769c206f3f1f02ece6feea0e17e34706d Mon Sep 17 00:00:00 2001 From: bokuweb Date: Thu, 9 Aug 2018 08:56:59 +0900 Subject: [PATCH 3/4] js-sys: Add extends attributes for js_sys::Number --- crates/js-sys/src/lib.rs | 1 + crates/js-sys/tests/wasm/Number.rs | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 30ec0855..9452d0a8 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -1385,6 +1385,7 @@ extern "C" { // Number. #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Number; diff --git a/crates/js-sys/tests/wasm/Number.rs b/crates/js-sys/tests/wasm/Number.rs index fee72abc..a79a57c6 100644 --- a/crates/js-sys/tests/wasm/Number.rs +++ b/crates/js-sys/tests/wasm/Number.rs @@ -1,9 +1,17 @@ use std::f64::{INFINITY, NAN}; +use wasm_bindgen::JsCast; use wasm_bindgen::JsValue; use wasm_bindgen_test::*; use js_sys::*; +#[wasm_bindgen_test] +fn number_inheritance() { + let number = Number::new(&JsValue::from(10)); + assert!(number.is_instance_of::()); + assert!(number.is_instance_of::()); +} + #[wasm_bindgen_test] fn is_finite() { assert!(Number::is_finite(&42.into())); From e9e707268746f93e2cc74e9b379565baa6a83350 Mon Sep 17 00:00:00 2001 From: sepiropht Date: Thu, 9 Aug 2018 14:33:59 +0200 Subject: [PATCH 4/4] [670] add extends for Array type --- crates/js-sys/tests/wasm/Array.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/js-sys/tests/wasm/Array.rs b/crates/js-sys/tests/wasm/Array.rs index 9525a913..d6c38954 100644 --- a/crates/js-sys/tests/wasm/Array.rs +++ b/crates/js-sys/tests/wasm/Array.rs @@ -1,5 +1,6 @@ use wasm_bindgen::JsValue; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; macro_rules! js_array { @@ -286,3 +287,9 @@ fn for_each() { assert_eq!(sum_indices_of_evens(&js_array![1, 3, 5, 7]), 0); assert_eq!(sum_indices_of_evens(&js_array![3, 5, 7, 10]), 3); } +#[wasm_bindgen_test] +fn array_inheritance() { + let array = Array::new(); + assert!(array.is_instance_of::()); + assert!(array.is_instance_of::()); +}