Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding in Promises #103

Merged
merged 40 commits into from
Feb 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
3f1d716
Initial work on adding in Promises
Pauan Feb 3, 2018
1b00354
Fixing minor nit
Pauan Feb 3, 2018
f605bab
Major refactoring
Pauan Feb 3, 2018
ebaaa35
Changing the done method to no longer return a new Promise
Pauan Feb 3, 2018
f2ab6d8
Adding in promisify method
Pauan Feb 3, 2018
61cb604
Changing to une unsync::oneshot
Pauan Feb 3, 2018
bc3809a
Removing Box from PromiseFuture
Pauan Feb 4, 2018
b656075
Initial work on the Promise Executor
Pauan Feb 5, 2018
7cb66f9
Adding in promise example
Pauan Feb 5, 2018
f416d96
Various cleanup
Pauan Feb 5, 2018
5c479e4
Adding in documentation
Pauan Feb 5, 2018
244701a
Removing some unused code
Pauan Feb 5, 2018
2a3385f
Renaming JSError to Error
Pauan Feb 5, 2018
eb30017
Adding in copyright license and attribution
Pauan Feb 5, 2018
a256a72
Adding in links to the official spec
Pauan Feb 5, 2018
73fd2a6
Fixing erroneous documentation
Pauan Feb 5, 2018
d598941
Fixing minor nit
Pauan Feb 5, 2018
13bebf8
Adding in spawn_print function
Pauan Feb 5, 2018
1ab6c2c
Changing the promise example to use spawn_print
Pauan Feb 5, 2018
56eafa1
Removing PromiseFuture::spawn_print
Pauan Feb 7, 2018
405b3b6
Fix Resubmission Panic in Executor
CryZe Feb 7, 2018
8bd5ba7
Minor refactoring of promise_executor.rs
Pauan Feb 7, 2018
a344b2e
Adding in tests for the Executor, and also fixing a deadlock with the…
Pauan Feb 8, 2018
560bcc1
Fixing memory unsafety
Pauan Feb 8, 2018
b100726
Adding in some code testing for panics
Pauan Feb 8, 2018
79d6c0b
Removing setTimeout from the Executor, it also now ignores multiple c…
Pauan Feb 9, 2018
7c8bd26
Renaming promisify to convert
Pauan Feb 9, 2018
410cca6
Adding in TODO note
Pauan Feb 9, 2018
f8b0118
Alternative executor implementation
Diggsey Feb 9, 2018
7520965
Reorder clearing of `is_queued` to optimize notifying finished tasks
Diggsey Feb 9, 2018
61f91aa
Adding in RefCell example
Pauan Feb 10, 2018
50a456e
Fixing nits
Pauan Feb 10, 2018
48924f9
Enabling the RefCell example
Pauan Feb 10, 2018
329a6e3
Fixing minor nit
Pauan Feb 10, 2018
eef311b
Changing to use the new ReferenceType deriving
Pauan Feb 11, 2018
73d9f85
Fixing minor nit
Pauan Feb 11, 2018
960f737
Changing PromiseFuture to allow for any type for the error
Pauan Feb 11, 2018
4475f8b
Renaming Promise::convert to Promise::from_thenable
Pauan Feb 11, 2018
ef37ec4
Minor improvement to the docs
Pauan Feb 11, 2018
eef539d
Fixing the cfg-gating for futures
Pauan Feb 11, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ serde = { version = "1", optional = true }
serde_json = { version = "1", optional = true }
clippy = { version = "0.0", optional = true }
stdweb-derive = { path = "stdweb-derive" }
futures = { version = "0.1.18", optional = true }

[dev-dependencies]
serde_json = "1"
serde_derive = "1"

[features]
default = ["serde", "serde_json"]
dev = ["serde", "serde_json", "clippy"]
default = ["serde", "serde_json", "futures"]
dev = ["serde", "serde_json", "futures", "clippy"]
serde-support = ["serde", "serde_json"]
nightly = []
web_test = []
Expand Down
8 changes: 8 additions & 0 deletions examples/promise/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "promise"
version = "0.1.0"
authors = ["Pauan <pcxunlimited@gmail.com>"]

[dependencies]
stdweb = { path = "../.." }
futures = "0.1.18"
Empty file added examples/promise/README.md
Empty file.
244 changes: 244 additions & 0 deletions examples/promise/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
#[macro_use]
extern crate stdweb;
extern crate futures;

use futures::Future;
use stdweb::unstable::{TryInto};
use stdweb::web::error::Error;
use stdweb::{Null, Promise, PromiseFuture};
use std::rc::Rc;
use std::cell::RefCell;
use futures::{Poll, Async};
use futures::task::{current, Task};


fn log( a: &str ) {
js! { @(no_return)
console.log( @{a} );
}
}


fn test_error_conversion() {
let a: PromiseFuture< Null, String > = js!( return Promise.reject( "hi!" ); ).try_into().unwrap();

PromiseFuture::spawn(
a.map( |_| () ).map_err( |x| {
log( &format!( "String error: {:#?}", x ) );
} )
);

let _a: PromiseFuture< Null, Error > = js!( return Promise.resolve( null ); ).try_into().unwrap();
log( "Null works" );

let _a: PromiseFuture< Null, Error > = js!( return Promise.reject( new Error( "hi!" ) ); ).try_into().unwrap();
log( "Error works" );

//let _a: PromiseFuture< Null, SyntaxError > = js!( return Promise.reject( new Error( "hi!" ) ); ).try_into().unwrap();
//log( "Error conversion fails" );
}


fn test_refcell() {
struct TaskA {
shared_state: Rc<RefCell<u32>>,
task_b: Task,
}

impl Future for TaskA {
type Item = ();
type Error = ();

fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
js! { console.log("Poll TaskA"); }

let foo = self.shared_state.borrow_mut();

js! { console.log(@{format!("TaskA 1: {:#?}", foo)}); }

self.task_b.notify();

js! { console.log(@{format!("TaskA 2: {:#?}", foo)}); }

Ok(Async::NotReady)
}
}

struct TaskB {
shared_state: Rc<RefCell<u32>>,
initialized: bool,
}

impl Future for TaskB {
type Item = ();
type Error = ();

fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
js! { console.log("Poll TaskB"); }

if !self.initialized {
self.initialized = true;

let task_b = current();

let foo = self.shared_state.borrow();

js! { console.log(@{format!("TaskB 1: {:#?}", foo)}); }

PromiseFuture::spawn(TaskA {
shared_state: self.shared_state.clone(),
task_b: task_b,
});
}

let foo = self.shared_state.borrow();

js! { console.log(@{format!("TaskB 1: {:#?}", foo)}); }

Ok(Async::NotReady)
}
}

PromiseFuture::spawn(TaskB {
shared_state: Rc::new(RefCell::new(0)),
initialized: false
});
}


fn test_panic() {
let promise: Promise = js!( return Promise.resolve(null); ).try_into().unwrap();

promise.done( |result: Result< Null, Error >| {
log( &format!( "Promise result: {:#?}", result ) );
panic!( "Testing panic!" );
} );
}


fn test_notify() {
struct MyFuture {
polls: u32,
count: u32,
done: bool,
receiver: futures::unsync::oneshot::Receiver< () >,
}

impl MyFuture {
fn new( count: u32 ) -> Self {
let ( sender, receiver ) = futures::unsync::oneshot::channel();

let callback = || {
log( "setTimeout done" );

log( &format!("Sending {:#?}", sender.send( () ) ) );
};

log( "setTimeout started" );

js! { @(no_return)
setTimeout( function () {
@{stdweb::Once( callback )}();
}, 1000 );
}

Self {
polls: 0,
count: count,
done: false,
receiver,
}
}
}

impl Future for MyFuture {
type Item = u32;
type Error = ();

fn poll( &mut self ) -> futures::Poll< Self::Item, Self::Error > {
self.polls += 1;

if !self.done {
match self.receiver.poll() {
Ok( futures::Async::Ready( () ) ) => self.done = true,

Ok( futures::Async::NotReady ) => {},

Err( _ ) => self.done = true,
}
}

if self.done {
if self.count == 0 {
Ok( futures::Async::Ready( self.polls ) )

} else {
self.count -= 1;

let task = futures::task::current();
task.notify();
task.notify();
task.notify();
task.notify();
task.notify();
task.notify();
task.notify();
task.notify();
task.notify();
task.notify();
task.notify();
task.notify();
task.notify();
task.notify();

Ok( futures::Async::NotReady )
}

} else {
Ok( futures::Async::NotReady )
}
}
}

PromiseFuture::spawn(
MyFuture::new( 5 ).map( |x| {
log( &format!( "MyFuture count: {}", x ) );
assert_eq!( x, 7 );
} )
);
}


fn test_timeout() {
fn sleep( ms: u32 ) -> PromiseFuture< Null, Error > {
js!( return new Promise( function ( success, failure ) {
setTimeout( function () {
success( null );
}, @{ms} );
} ); ).try_into().unwrap()
}

PromiseFuture::spawn(
sleep( 2000 ).inspect( |_| log( "Timeout 1 done!") ).join(
sleep( 2000 ).inspect( |_| log( "Timeout 2 done!" ) ) )
.and_then( |_|
sleep( 1000 ).inspect( |_| log( "Timeout 3 done!") ) )
.and_then( |_|
futures::future::err( Error::new( "Testing error!" ) ) )
.map_err( |e| e.print() )
);
}


fn main() {
stdweb::initialize();

test_refcell();
test_panic();
test_notify();
test_timeout();
test_error_conversion();

stdweb::event_loop();
}

8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ extern crate stdweb_internal_macros;
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
pub use stdweb_internal_macros::js_export;

#[cfg(feature = "futures")]
extern crate futures;

#[macro_use]
extern crate stdweb_derive;

Expand Down Expand Up @@ -128,6 +131,11 @@ pub use webcore::instance_of::InstanceOf;
pub use webcore::reference_type::ReferenceType;
pub use webcore::serialization::JsSerialize;

pub use webcore::promise::Promise;

#[cfg(feature = "futures")]
pub use webcore::promise_future::PromiseFuture;

#[cfg(feature = "serde")]
/// A module with serde-related APIs.
pub mod serde {
Expand Down
20 changes: 20 additions & 0 deletions src/webapi/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,26 @@ pub trait IError: ReferenceType {
#[reference(instance_of = "Error")]
pub struct Error( Reference );

impl Error {
/// Creates a new `Error` with the specified `description`.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)
#[inline]
pub fn new( description: &str ) -> Self {
js!( return new Error( @{description} ); ).try_into().unwrap()
}

/// Prints the `Error` to the console (this prints the error's description and also its stack trace).
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Console/error)
#[inline]
pub fn print( &self ) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we remove this method?

I understand the need to have convenience methods, but I'd prefer if we'd just wrap console.error itself so that you could do console::error(e).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair, though I had kind of figured that console.error(e) was a placeholder, and we might implement better error handling in the future, in which case having e.print() would be nice. But I don't have a particularly strong opinion about it, so I don't mind removing the print method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't currently have a console::error function, I'd rather leave this in for now. Afterwards I'll make another pull request which adds in console::error and removes print

js! { @(no_return)
console.error( @{self} );
}
}
}

// Error specification:
// https://www.ecma-international.org/ecma-262/6.0/#sec-error-objects

Expand Down
7 changes: 7 additions & 0 deletions src/webcore/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ pub mod unsafe_typed_array;
pub mod once;
pub mod instance_of;
pub mod reference_type;
pub mod promise;

#[cfg(feature = "futures")]
pub mod promise_future;

#[cfg(feature = "futures")]
pub mod promise_executor;

#[cfg(feature = "nightly")]
pub mod void {
Expand Down
Loading