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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
use std;
use std::fmt;
use std::marker::PhantomData;

use discard::Discard;
use webcore::once::Once;
use webcore::value::{Value, Reference};
use webcore::try_from::{TryInto, TryFrom};
use webcore::discard::DiscardOnDrop;

#[cfg(feature = "futures-support")]
use webcore::serialization::JsSerialize;

#[cfg(feature = "futures-support")]
use futures_core::TryFuture;

#[cfg(feature = "futures-support")]
use futures_util::{FutureExt, TryFutureExt};

#[cfg(feature = "futures-support")]
use futures_util::future::ready;

#[cfg(feature = "futures-support")]
use futures_channel::oneshot::channel;

#[cfg(feature = "futures-support")]
use super::promise_future::{PromiseFuture, spawn_local};


/// This is used to cleanup the [`done`](struct.Promise.html#method.done) callback.
///
/// See the documentation for [`done`](struct.Promise.html#method.done) for more information.
#[derive( Debug, Clone )]
pub struct DoneHandle {
    state: Value,
}

impl Discard for DoneHandle {
    fn discard( self ) {
        js! { @(no_return)
            var state = @{&self.state};
            state.cancelled = true;
            state.callback.drop();
        }
    }
}


/// A `Promise` object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
///
/// In most situations you shouldn't use this, use [`PromiseFuture`](struct.PromiseFuture.html) instead.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
// https://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects
#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
#[reference(instance_of = "Promise")]
pub struct Promise( Reference );

impl Promise {
    // https://www.ecma-international.org/ecma-262/6.0/#sec-promise-resolve-functions
    fn is_thenable( input: &Reference ) -> bool {
        (js! {
            var input = @{input};
            // This emulates the `Type(input) is Object` and `IsCallable(input.then)` ECMAScript abstract operations.
            return Object( input ) === input &&
                   typeof input.then === "function";
        }).try_into().unwrap()
    }

    /// This function should rarely be needed, use [`PromiseFuture`](struct.PromiseFuture.html) instead.
    ///
    /// This function is needed if you have a JavaScript value which is a Promise-like object
    /// (it has a `then` method) but it isn't a true `Promise`.
    ///
    /// That situation is rare, but it can happen if you are using a Promise library such as jQuery or
    /// Bluebird.
    ///
    /// In that situation you can use `Promise::from_thenable` to convert it into a true `Promise`.
    ///
    /// If the `input` isn't a Promise-like object then it returns `None`.
    ///
    /// # Examples
    ///
    /// Convert a Promise-like object to a `Promise`:
    ///
    /// ```rust
    /// // jQuery Promise
    /// Promise::from_thenable(&js!( return $.get("test.php"); ).try_into().unwrap())
    ///
    /// // Bluebird Promise
    /// Promise::from_thenable(&js!( return bluebird_promise.timeout(1000); ).try_into().unwrap())
    /// ```
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve)
    // https://www.ecma-international.org/ecma-262/6.0/#sec-promise.resolve
    // https://www.ecma-international.org/ecma-262/6.0/#sec-promise-resolve-functions
    // https://www.ecma-international.org/ecma-262/6.0/#sec-promiseresolvethenablejob
    pub fn from_thenable( input: &Reference ) -> Option< Self > {
        // TODO this can probably be made more efficient
        if Promise::is_thenable( input ) {
            Some( js!( return Promise.resolve( @{input} ); ).try_into().unwrap() )

        } else {
            None
        }
    }

    /// This function converts a Rust Future into a JavaScript Promise.
    ///
    /// This is needed when you want to pass a Rust Future into JavaScript.
    ///
    /// If you simply want to use a JavaScript Promise inside Rust, then you
    /// don't need to use this function: you should use
    /// [`PromiseFuture`](struct.PromiseFuture.html) instead.
    ///
    /// # Examples
    ///
    /// Convert a Rust Future into a JavaScript Promise:
    ///
    /// ```rust
    /// Promise::from_future(rust_future)
    /// ```
    ///
    /// Export a Rust Future so that it can be used in JavaScript
    /// (this only works with the `wasm32-unknown-unknown` target):
    ///
    /// ```rust
    /// #[js_export]
    /// fn foo() -> Promise {
    ///     Promise::from_future(rust_future)
    /// }
    /// ```
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Syntax)
    // https://www.ecma-international.org/ecma-262/6.0/#sec-promise-executor
    #[cfg(feature = "futures-support")]
    pub fn from_future< A >( future: A ) -> Self
        where A: TryFuture + 'static,
              A::Ok: JsSerialize,
              A::Error: JsSerialize {

        let future = future.into_future();

        #[inline]
        fn call< A: JsSerialize >( f: Reference, value: A ) {
            js! { @(no_return) @{f}( @{value} ); }
        }

        let callback = move |success: Reference, error: Reference| {
            spawn_local(
                future.then( move |result| {
                    match result {
                        Ok( a ) => call( success, a ),
                        Err( a ) => call( error, a ),
                    }

                    ready( () )
                } )
            );
        };

        js!( return new Promise( @{Once( callback )} ); ).try_into().unwrap()
    }

    /// This method is usually not needed, use [`PromiseFuture`](struct.PromiseFuture.html) instead.
    ///
    /// When the `Promise` either succeeds or fails, it calls the `callback` with the result.
    ///
    /// It does not wait for the `Promise` to succeed / fail (it does not block the thread).
    ///
    /// The `callback` is guaranteed to be called asynchronously even if the `Promise` is already succeeded / failed.
    ///
    /// If the `Promise` never succeeds / fails then the `callback` will never be called.
    ///
    /// This method returns a [`DoneHandle`](struct.DoneHandle.html). The [`DoneHandle`](struct.DoneHandle.html)
    /// *exclusively* owns the `callback`, so when the [`DoneHandle`](struct.DoneHandle.html) is dropped it will
    /// drop the `callback` and the `callback` will never be called. This will happen even if the `Promise` is not dropped!
    ///
    /// Dropping the [`DoneHandle`](struct.DoneHandle.html) does ***not*** cancel the `Promise`, because promises
    /// do not support cancellation.
    ///
    /// If you are no longer interested in the `Promise`'s result you can simply drop the [`DoneHandle`](struct.DoneHandle.html)
    /// and then the `callback` will never be called.
    ///
    /// But if you *are* interested in the `Promise`'s result, then you have two choices:
    ///
    /// * Keep the [`DoneHandle`](struct.DoneHandle.html) alive until after the `callback` is called (by storing it in a
    ///   variable or data structure).
    ///
    /// * Use the [`leak`](struct.DiscardOnDrop.html#method.leak) method to leak the [`DoneHandle`](struct.DoneHandle.html).
    ///   If the `Promise` never succeeds or fails then this ***will*** leak the memory of the callback, so only use
    ///   [`leak`](struct.DiscardOnDrop.html#method.leak) if you need to.
    ///
    /// # Examples
    ///
    /// Normal usage:
    ///
    /// ```rust
    /// let handle = promise.done(|result| {
    ///     match result {
    ///         Ok(success) => { ... },
    ///         Err(error) => { ... },
    ///     }
    /// });
    /// ```
    ///
    /// Leak the [`DoneHandle`](struct.DoneHandle.html) and `callback`:
    ///
    /// ```rust
    /// promise.done(|result| {
    ///     match result {
    ///         Ok(success) => { ... },
    ///         Err(error) => { ... },
    ///     }
    /// }).leak();
    /// ```
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then)
    // https://www.ecma-international.org/ecma-262/6.0/#sec-performpromisethen
    pub fn done< A, B, F >( &self, callback: F ) -> DiscardOnDrop< DoneHandle >
        where A: TryFrom< Value >,
              B: TryFrom< Value >,
              // TODO these Debug constraints are only needed because of unwrap
              A::Error: std::fmt::Debug,
              B::Error: std::fmt::Debug,
              F: FnOnce( Result< A, B > ) + 'static {

        let callback = move |value: Value, success: bool| {
            let value: Result< A, B > = if success {
                // TODO figure out a way to avoid the unwrap
                let value: A = value.try_into().unwrap();
                Ok( value )

            } else {
                // TODO figure out a way to avoid the unwrap
                let value: B = value.try_into().unwrap();
                Err( value )
            };

            callback( value );
        };

        let state = js!(
            var callback = @{Once( callback )};

            var state = {
                cancelled: false,
                callback: callback
            };

            // TODO don't swallow any errors thrown inside callback
            @{self}.then( function ( value ) {
                if ( !state.cancelled ) {
                    callback( value, true );
                }
            }, function ( value ) {
                if ( !state.cancelled ) {
                    callback( value, false );
                }
            } );

            return state;
        );

        DiscardOnDrop::new( DoneHandle {
            state,
        } )
    }

    /// This method converts the `Promise` into a [`PromiseFuture`](struct.PromiseFuture.html), so that it can be used as a Rust
    /// [`Future`](https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.5/futures/future/trait.Future.html).
    ///
    /// This method should rarely be needed, instead use [`value.try_into()`](unstable/trait.TryInto.html) to convert directly
    /// from a [`Value`](enum.Value.html) into a [`PromiseFuture`](struct.PromiseFuture.html).
    ///
    /// # Examples
    ///
    /// ```rust
    /// promise.to_future().map(|x| x + 1)
    /// ```
    // We can't use the IntoFuture trait because Promise doesn't have a type argument
    // TODO explain more why we can't use the IntoFuture trait
    #[cfg(feature = "futures-support")]
    pub fn to_future< A, B >( &self ) -> PromiseFuture< A, B >
         where A: TryFrom< Value > + 'static,
               B: TryFrom< Value > + 'static,
               // TODO remove these later
               A::Error: std::fmt::Debug,
               B::Error: std::fmt::Debug {

        let ( sender, receiver ) = channel();

        PromiseFuture {
            future: receiver,
            _done_handle: self.done( |value| {
                // TODO is this correct ?
                match sender.send( value ) {
                    Ok( _ ) => {},
                    Err( _ ) => {},
                };
            } ),
        }
    }
}

/// A statically typed `Promise`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TypedPromise< T, E >( Promise, PhantomData< (T, E) > );

impl< T, E > TypedPromise< T, E >
    where T: TryFrom< Value >,
          E: TryFrom< Value >
{
    #[inline]
    pub(crate) fn new( promise: Promise ) -> Self {
        TypedPromise( promise, PhantomData )
    }

    /// A strongly typed version of [`Promise.done`](struct.Promise.html#method.done).
    #[inline]
    pub fn done< F >( &self, callback: F ) -> DiscardOnDrop< DoneHandle >
        where F: FnOnce( Result< T, E > ) + 'static,
              T::Error: fmt::Debug,
              E::Error: fmt::Debug
    {
        self.0.done( move |result| callback( result ) )
    }
}

impl< T, E > From< TypedPromise< T, E > > for Promise {
    #[inline]
    fn from( promise: TypedPromise< T, E > ) -> Promise {
        promise.0
    }
}