1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use std;
use webcore::value::{Value, ConversionError};
use webcore::try_from::{TryInto, TryFrom};
use webapi::error;
use futures_core::{Future, Poll, Async, Never};
use futures_core::task::Context;
use futures_channel::oneshot::Receiver;
use webcore::executor::spawn_local;
use webcore::discard::DiscardOnDrop;
use webcore::serialization::JsSerialize;
use super::promise::{Promise, DoneHandle};
/// This allows you to use a JavaScript [`Promise`](struct.Promise.html) as if it is a Rust [`Future`](https://docs.rs/futures/0.2.*/futures/future/trait.Future.html).
///
/// The preferred way to create a `PromiseFuture` is to use [`value.try_into()`](unstable/trait.TryInto.html) on a JavaScript [`Value`](enum.Value.html).
///
/// # Examples
///
/// Convert a JavaScript `Promise` into a `PromiseFuture`:
///
/// ```rust
/// let future: PromiseFuture<String> = js!( return Promise.resolve("foo"); ).try_into().unwrap();
/// ```
pub struct PromiseFuture< Value, Error = error::Error > {
pub(crate) future: Receiver< Result< Value, Error > >,
pub(crate) _done_handle: DiscardOnDrop< DoneHandle >,
}
impl PromiseFuture< (), Never > {
/// Asynchronously runs the [`Future`](https://docs.rs/futures/0.2.*/futures/future/trait.Future.html) on the current thread
/// and then immediately returns. This does *not* block the current thread.
///
/// This function is normally called once in `main`, it is usually not needed to call it multiple times.
///
/// The only way to retrieve the value of the future is to use the various
/// [`FutureExt`](https://docs.rs/futures/0.2.*/futures/future/trait.FutureExt.html) methods, such as
/// [`map`](https://docs.rs/futures/0.2.*/futures/future/trait.FutureExt.html#method.map) or
/// [`inspect`](https://docs.rs/futures/0.2.*/futures/future/trait.FutureExt.html#method.inspect).
///
/// In addition, you must handle all errors yourself. Because the errors happen asynchronously, the only way to catch them is
/// to use a [`FutureExt`](https://docs.rs/futures/0.2.*/futures/future/trait.FutureExt.html) method, such as
/// [`map_err`](https://docs.rs/futures/0.2.*/futures/future/trait.FutureExt.html#method.map_err).
///
/// It is very common to want to print the errors to the console. You can do that by using `.map_err(PromiseFuture::print_error_panic)`
///
/// # Examples
///
/// Asynchronously run a future in `main`, printing any errors to the console:
///
/// ```rust
/// fn main() {
/// PromiseFuture::spawn_local(
/// create_some_future()
/// .map_err(PromiseFuture::print_error_panic)
/// );
/// }
/// ```
///
/// Use the output value of the future:
///
/// ```rust
/// fn main() {
/// PromiseFuture::spawn_local(
/// create_some_future()
/// .map(|x| {
/// println!("Future finished with value: {:#?}", x);
/// })
/// .map_err(PromiseFuture::print_error_panic)
/// );
/// }
/// ```
///
/// Catch errors and handle them yourself:
///
/// ```rust
/// fn main() {
/// PromiseFuture::spawn_local(
/// create_some_future()
/// .map_err(|e| handle_error_somehow(e))
/// );
/// }
/// ```
#[inline]
pub fn spawn_local< B >( future: B ) where
B: Future< Item = (), Error = Never > + 'static {
spawn_local( future );
}
/// Prints an error to the console and then panics.
///
/// See the documentation for [`spawn_local`](#method.spawn_local) for more details.
///
/// # Panics
/// This function *always* panics.
#[inline]
pub fn print_error_panic< A: JsSerialize >( value: A ) -> Never {
js! { @(no_return)
console.error( @{value} );
}
panic!();
}
}
impl< A, B > std::fmt::Debug for PromiseFuture< A, B > {
fn fmt( &self, formatter: &mut std::fmt::Formatter ) -> std::fmt::Result {
formatter.debug_struct( "PromiseFuture" ).finish()
}
}
impl< A, B > Future for PromiseFuture< A, B > {
type Item = A;
type Error = B;
fn poll( &mut self, cx: &mut Context ) -> Poll< Self::Item, Self::Error > {
// TODO maybe remove this unwrap ?
match self.future.poll( cx ).unwrap() {
Async::Ready( Ok( a ) ) => Ok( Async::Ready( a ) ),
Async::Ready( Err( e ) ) => Err( e ),
Async::Pending => Ok( Async::Pending ),
}
}
}
impl< A, B > TryFrom< Value > for PromiseFuture< A, B >
where A: TryFrom< Value > + 'static,
B: TryFrom< Value > + 'static,
// TODO remove this later
A::Error: std::fmt::Debug,
B::Error: std::fmt::Debug {
type Error = ConversionError;
fn try_from( v: Value ) -> Result< Self, Self::Error > {
let promise: Promise = v.try_into()?;
Ok( promise.to_future() )
}
}