/**
 * Promise implementation in TypeScript.
 *
 * Copyright (C) 2015 Martin Poelstra
 * License: MIT
 */ import async from "./async" ; import { defaultPossiblyUnhandledRejectionHandledHandler , defaultPossiblyUnhandledRejectionHandler , defaultUnhandledRejectionHandler , } from "./rejections" ; import Trace from "./Trace" ; import { assert } from "./util" ; export interface Thenable < T > { then < R1 = T , R2 = never > ( this , onfulfilled ? , onrejected ? ) : Thenable < R1 | R2 > ; then < R1 = T > ( this , onfulfilled ? , onrejected ? ) : Thenable < R1 | void > ; } /**
 * A ts-promise implements the 'synchronous inspection' interface which allows
 * to synchronously determine the promise's current state.
 */ export interface Inspection < T > { return isFulfilled ( ) : boolean ; return isRejected ( ) : boolean ; return isPending ( ) : boolean ; return value ( ) : T ; return reason ( ) : any ; } let trace ; let longTraces = false ; const enum State { Pending , Fulfilled , Rejected } /**
 * Bit flags about a Promise's internal state.
 */ const enum Flags { RejectionHandled = 1 , UnhandledRejectionNotified = 2 } function internalResolver ( fulfill ? , reject ? ) { } internalResolver ( undefined , undefined ) ; function noop ( ) { } interface GetThenError { error : any ; } const getThenError = { error : undefined , } ; function wrapNonError ( a ) { return a ; } type FulfillmentHandler < T , R > = ( ( value : T ) => R | Thenable < R > ) | null | undefined ; type RejectionHandler < R > = ( ( reason : any ) => R | Thenable < R > ) | null | undefined ; /**
 * Subscription to be notified when promise resolves.
 *
 * When a Handler is attached to an unresolved promise, it's added to its queue,
 * which is flushed as soon as the promise resolves.
 * When a Handler is attached to a resolved promise, it's scheduled immediately
 * (but still asynchronously).
 *
 * A Handler is used for 3 different cases:
 * 1. calling the onFulfilled/onRejected callbacks of .then()
 * 2. calling the onFulfilled/onRejected callbacks of .done()
 * 3. waiting for a promise returned from an onFulfilled/onRejected callback to
 *    be resolved (i.e. that promise is 'following' us)
 *
 * `promise` is always the promise that was resolved, i.e. the 'source'
 * onFulfilled/onRejected are the callbacks of the .then()/.done(), or both
 * `undefined` in case of a follower (case 3).
 * In all cases, `slave` is the promise that depends on our result, and/or the
 * callback's result.
 */ interface Handler < T , R > { promise : Promise < T > ; onFulfilled : FulfillmentHandler < T , R > ; onRejected : RejectionHandler < R > ; slave : Promise < R > | undefined ; done : Trace | undefined ; } const dummyDoneTrace = new Trace ( ) ; /**
 * Combination of a promise and its resolve/reject functions.
 * Created using Promise.defer().
 *
 * It is generally better (and slightly faster) to use the Promise
 * constructor to create a promise, as that will also catch any exception
 * thrown while running the resolver.
 *
 * A Deferred can be useful in some scenarios though, e.g. when working with
 * timers, protocol request/response pairs, etc.
 */ export interface Deferred < T > { /**
     * Initially unresolved promise, resolved by the resolve or reject
     * function on this object.
     */ promise : Promise < T > ; /**
     * Reject corresponding promise.
     * The first call to either resolve or reject resolves the promise, any
     * other calls are ignored.
     * This function is a free function (i.e. not a 'method' on this object).
     */ reject : ( reason : Error ) => void ; /**
     * Resolve corresponding promise.
     * The first call to either resolve or reject resolves the promise, any
     * other calls are ignored.
     * This function is a free function (i.e. not a 'method' on this object).
     * Note: resolving with a rejected Thenable leads to a rejected promise.
     */ resolve : ( value : T | Thenable < T > ) => void ; } /**
 * Convenience version of Deferred that allows calling resolve() without an
 * argument.
 *
 * Deferred is a combination of a promise and its resolve/reject functions.
 * Created using Promise.defer().
 *
 * It is generally better (and slightly faster) to use the Promise
 * constructor to create a promise, as that will also catch any exception
 * thrown while running the resolver.
 *
 * A Deferred can be useful in some scenarios though, e.g. when working with
 * timers, protocol request/response pairs, etc.
 */ export interface VoidDeferred extends Deferred < void > { /**
     * Resolve corresponding promise.
     * The first call to either resolve or reject resolves the promise, any
     * other calls are ignored.
     * This function is a free function (i.e. not a 'method' on this object).
     * Note: resolving with a rejected Thenable leads to a rejected promise.
     */ resolve : ( value ? : void | Thenable < void > ) => void ; } /**
 * Currently unwrapping promise, while running one of its then-callbacks.
 * Used to set the source of newly created promises.
 * We guarantee that at most one callback of a then() is running at any time.
 */ let unwrappingPromise ; let promiseIdCounter = 0 ; /**
 * Generic Error class descriptor.
 *
 * Allows to pass classes to e.g. `Promise.catch()` which derive from Error.
 */ export type ErrorClass = new ( ... args ) => Error ; export type UnhandledRejectionHandler = ( reason : any , doneTrace : Trace ) => void ; export type PossiblyUnhandledRejectionHandler = ( promise : Promise < any > ) => void ; export type PossiblyUnhandledRejectionHandledHandler = ( promise : Promise < any > ) => void ; /**
 * Fast, robust, type-safe promise implementation.
 */ export class Promise < T > implements Thenable < T > , Inspection < T > { private _id : number = promiseIdCounter ++ ; private _state : State = State . Pending ; private _result : any = undefined ; private _handlers : Array < Handler < T , any > > | undefined = undefined ; private _flags : number = 0 ; private _trace : Trace | undefined = undefined ; private static _onUnhandledRejectionHandler : UnhandledRejectionHandler ; private static _onPossiblyUnhandledRejectionHandler : PossiblyUnhandledRejectionHandler ; private static _onPossiblyUnhandledRejectionHandledHandler : PossiblyUnhandledRejectionHandledHandler ; param resolver constructor ( resolver ) { trace && trace ( this , "construct" ) ; if ( longTraces ) { this . _trace = new Trace ( Promise ) ; if ( unwrappingPromise ) { this . _setSource ( unwrappingPromise ) ; } } if ( resolver === internalResolver ) { return ; } if ( typeof resolver !== "function" ) { throw new TypeError ( "Promise resolver is not a function" ) ; } let called = false ; try { resolver ( ( y ) : void => { if ( called ) { return ; } called = true ; this . _resolve ( y ) ; } , ( r ) : void => { if ( called ) { return ; } called = true ; this . _reject ( wrapNonError ( r ) ) ; } ) ; } catch ( e ) { if ( ! called ) { called = true ; this . _reject ( wrapNonError ( e ) ) ; } } } param onFulfilled param onRejected return public then < R1 = T , R2 = never > ( onFulfilled , onRejected ? ) { trace && trace ( this , `then(${ typeof onFulfilled }, ${ typeof onRejected })` ) ; if ( this . _state === State . Fulfilled && typeof onFulfilled !== "function" || this . _state === State . Rejected && typeof onRejected !== "function" ) { return < Promise < any > > this ; } const slave = new ( Object . getPrototypeOf ( this ) . constructor ) ( internalResolver ) ; slave . _setSource ( this ) ; this . _enqueue ( onFulfilled , onRejected , slave , undefined ) ; return slave ; } param onFulfilled param onRejected public done < R > ( onFulfilled ? , onRejected ? ) { trace && trace ( this , `done(${ typeof onFulfilled }, ${ typeof onRejected })` ) ; if ( this . _state === State . Fulfilled && typeof onFulfilled !== "function" ) { return ; } let doneTrace = dummyDoneTrace ; if ( longTraces ) { doneTrace = new Trace ( ) ; if ( this . _trace ) { doneTrace . setSource ( this . _trace ) ; } } this . _enqueue ( onFulfilled , onRejected , undefined , doneTrace ) ; } param onRejected return public catch < R > ( onRejected ) ; param predicate param onRejected return public catch < R > ( predicate , onRejected ) ; param predicate param onRejected return public catch < R > ( predicate , onRejected ) ; param predicate param onRejected return public catch < R > ( ) { if ( arguments . length === 1 ) { const onRejected = arguments [ 0 ] ; return this . then ( undefined , onRejected ) ; } else { const predicate = arguments [ 0 ] ; const onRejected = arguments [ 1 ] ; return this . then ( undefined , ( reason ) => { let match = false ; if ( typeof predicate === "function" ) { if ( predicate . prototype instanceof Error || predicate === Error ) { match = reason instanceof predicate ; } else { match = predicate ( reason ) ; } } else if ( Array . isArray ( predicate ) ) { for ( const p of predicate ) { if ( reason instanceof p ) { match = true ; break ; } } } else { throw new TypeError ( "invalid predicate to .catch(), got " + typeof predicate ) ; } if ( match ) { return onRejected ( reason ) ; } return Promise . reject < T | R > ( reason ) ; } ) ; } } param handler return public finally ( handler ) { const runner = ( ) => handler ( this ) ; return this . then ( runner , runner ) . return ( this ) ; } return public isFulfilled ( ) { return this . _state === State . Fulfilled ; } return public isRejected ( ) { return this . _state === State . Rejected ; } return public isPending ( ) { return this . _state === State . Pending ; } return public value ( ) { if ( ! this . isFulfilled ( ) ) { throw new Error ( "Promise is not fulfilled" ) ; } return this . _result ; } return public reason ( ) { if ( ! this . isRejected ( ) ) { throw new Error ( "Promise is not rejected" ) ; } return this . _result ; } /**
     * Prevent this promise from throwing a PossiblyUnhandledRejection in
     * case it becomes rejected. Useful when the rejection will be handled later
     * (i.e. after the current 'tick'), or when the rejection is to be ignored
     * completely.
     *
     * This is equivalent to calling `.catch(() => {})`, but more efficient.
     *
     * Note: any derived promise (e.g. by calling `.then(cb)`) causes a new
     * promise to be created, which can still cause the rejection to be thrown.
     *
     * Note: if the rejection was already notified, the rejection-handled handler
     * will be called.
     */ public suppressUnhandledRejections ( ) { this . _setRejectionHandled ( ) ; } return public inspect ( ) { return this . toString ( ) ; } return public toString ( ) { let state ; switch ( this . _state ) { case State . Pending : state = "pending" ; break ; case State . Fulfilled : state = "fulfilled" ; break ; case State . Rejected : state = "rejected" ; break ; default : state = "unknown" ; } return `[Promise ${ this . _id }: ${ state }]` ; } param ms return public delay ( ms ) { return this . then ( ( value ) => { return new Promise < T > ( ( resolve ) => { setTimeout ( ( ) => resolve ( value ) , ms ) ; } ) ; } ) ; } param value return public return < R > ( value ) ; return public return ( ) ; param value return public return < R > ( value ? ) { if ( value === undefined ) { return this . then < any > ( noop ) ; } return this . then ( ( ) => value ) ; } param reason return public throw ( reason ) { return this . then ( ( ) => Promise . reject < T > ( reason ) ) ; } private _setSource ( source ) { if ( ! this . _trace || ! source . _trace ) { return ; } this . _trace . setSource ( source . _trace ) ; } private _resolve ( x ) { assert ( this . _state === State . Pending ) ; if ( ! x ) { this . _fulfill ( < T > x ) ; return ; } if ( this === x ) { this . _reject ( new TypeError ( "cannot resolve Promise to self" ) ) ; return ; } if ( x instanceof Promise ) { x . _setSource ( this ) ; x . _setRejectionHandled ( ) ; if ( x . _state === State . Pending ) { this . _followPromise ( x ) ; } else if ( x . _state === State . Fulfilled ) { this . _fulfill ( x . _result ) ; } else { this . _reject ( x . _result ) ; } return ; } if ( typeof x === "object" || typeof x === "function" ) { const then = this . _tryGetThen ( x ) ; if ( then === getThenError ) { this . _reject ( wrapNonError ( getThenError . error ) ) ; return ; } if ( typeof then === "function" ) { this . _followThenable ( < Thenable < T > > x , then ) ; return ; } } this . _fulfill ( < T > x ) ; } private _tryGetThen ( x ) { try { const then = ( < any > x ) . then ; return then ; } catch ( e ) { getThenError . error = e ; return getThenError ; } } private _fulfill ( value ) { assert ( this . _state === State . Pending ) ; trace && trace ( this , `_fulfill(${ typeof value })` ) ; this . _state = State . Fulfilled ; this . _result = value ; this . _flush ( ) ; } private _reject ( reason ) { assert ( this . _state === State . Pending ) ; trace && trace ( this , `_reject(${ reason })` ) ; this . _state = State . Rejected ; this . _result = reason ; if ( this . _trace && this . _result instanceof Error && ! ( < any > this . _result ) . trace ) { const stackTrace = this . _trace ; ( < any > this . _result ) . trace = stackTrace ; const originalStack = this . _result . stack ; if ( originalStack ) { Object . defineProperty ( this . _result , "stack" , { enumerable : false , get : ( ) : string => originalStack + "\n  from Promise at:\n" + stackTrace . inspect ( ) , } ) ; } } if ( ! ( this . _flags & Flags . RejectionHandled ) ) { async . enqueueIdle ( Promise . _unhandledRejectionChecker , this ) ; } this . _flush ( ) ; } private _setRejectionHandled ( ) { if ( ! ( this . _flags & Flags . RejectionHandled ) && ( this . _flags & Flags . UnhandledRejectionNotified ) ) { async . enqueue ( Promise . _onPossiblyUnhandledRejectionHandledHandler , this ) ; } this . _flags |= Flags . RejectionHandled ; trace && trace ( this , "rejectionHandled" ) ; } private _doCheckUnhandledRejection ( ) { if ( ! ( this . _flags & Flags . RejectionHandled ) && ! ( this . _flags & Flags . UnhandledRejectionNotified ) ) { this . _flags |= Flags . UnhandledRejectionNotified ; async . enqueue ( Promise . _onPossiblyUnhandledRejectionHandler , this ) ; } } private _followPromise ( slave ) { assert ( this . _state === State . Pending ) ; trace && trace ( this , `_follow([Promise ${ slave . _id }])` ) ; slave . _enqueue ( undefined , undefined , this , undefined ) ; } private _followThenable ( slave , then ) { assert ( this . _state === State . Pending ) ; trace && trace ( this , "_follow([Thenable])" ) ; let called = false ; try { then . call ( slave , ( y ) : void => { if ( called ) { return ; } called = true ; this . _resolve ( y ) ; } , ( r ) : void => { if ( called ) { return ; } called = true ; this . _reject ( wrapNonError ( r ) ) ; } ) ; } catch ( e ) { if ( ! called ) { called = true ; this . _reject ( wrapNonError ( e ) ) ; } } } private _enqueue ( onFulfilled , onRejected , slave , done ) { const h = { promise : this , onFulfilled , onRejected , slave , done , } ; if ( this . _state !== State . Pending ) { async . enqueue ( Promise . _unwrapper , h ) ; } else { if ( ! this . _handlers ) { this . _handlers = [ h ] ; } else { const i = this . _handlers . length ; this . _handlers [ i ] = h ; } } this . _setRejectionHandled ( ) ; } /**
     * Schedule any pending .then()/.done() callbacks and follower-promises to
     * be called/resolved.
     * Clears our queue, any callbacks/followers attached after this will be
     * scheduled without going through our handlers queue.
     */ private _flush ( ) { if ( ! this . _handlers ) { return ; } let i = 0 ; const h = this . _handlers ; const l = h . length ; this . _handlers = undefined ; while ( i < l ) { async . enqueue ( Promise . _unwrapper , h [ i ++ ] ) ; } } param handler private _unwrap ( handler ) { const callback = this . _state === State . Fulfilled ? handler . onFulfilled : handler . onRejected ; if ( handler . done ) { trace && trace ( this , `_unwrap()` ) ; if ( typeof callback !== "function" ) { if ( this . _state === State . Rejected ) { Promise . _onUnhandledRejectionHandler ( this . _result , handler . done ) ; } return ; } assert ( ! unwrappingPromise ) ; unwrappingPromise = this ; try { const result = callback ( this . _result ) ; if ( result ) { const p = ( result instanceof Promise ) ? result : Promise . resolve ( result ) ; p . done ( ) ; } unwrappingPromise = undefined ; } catch ( e ) { unwrappingPromise = undefined ; Promise . _onUnhandledRejectionHandler ( e , handler . done ) ; } return ; } const slave = handler . slave ! ; trace && trace ( this , `_unwrap(${ slave . _id })` ) ; if ( typeof callback === "function" ) { assert ( ! unwrappingPromise ) ; unwrappingPromise = slave ; try { slave . _resolve ( callback ( this . _result ) ) ; } catch ( e ) { slave . _reject ( wrapNonError ( e ) ) ; } unwrappingPromise = undefined ; } else { if ( this . _state === State . Fulfilled ) { slave . _fulfill ( this . _result ) ; } else { slave . _reject ( this . _result ) ; } } } param value return public static resolve < R > ( value ) ; return public static resolve ( ) ; param value return public static resolve < R > ( value ? ) { const p = new Promise ( internalResolver ) ; p . _resolve ( value ) ; return p ; } param reason return public static reject ( reason ) ; param reason return public static reject < T > ( reason ) ; param reason return public static reject < T > ( reason ) { const p = new Promise ( internalResolver ) ; p . _reject ( reason ) ; return p ; } param thenables return public static all < X > ( thenables ) { return new Promise < X [ ] > ( ( resolve , reject ) : void => { assert ( Array . isArray ( thenables ) , "thenables must be an Array" ) ; if ( thenables . length === 0 ) { resolve ( [ ] ) ; return ; } const result = new Array ( thenables . length ) ; let remaining = thenables . length ; for ( let i = 0 ; i < thenables . length ; i ++ ) { follow ( thenables [ i ] , i ) ; } function follow ( t , index ) { const slave = t instanceof Promise ? t : Promise . resolve ( t ) ; slave . done ( ( v ) : void => { result [ index ] = v ; remaining -- ; if ( remaining === 0 ) { resolve ( result ) ; } } , ( reason ) : void => reject ( reason ) ) ; } } ) ; } param thenables return public static race < X > ( thenables ) { return new Promise < X > ( ( resolve , reject ) : void => { assert ( Array . isArray ( thenables ) , "thenables must be an Array" ) ; for ( const t of thenables ) { const slave = t instanceof Promise ? t : Promise . resolve ( t ) ; Promise . resolve ( slave ) . done ( resolve , reject ) ; } } ) ; } return public static defer ( ) ; return public static defer < X > ( ) ; return public static defer < X > ( ) { let resolve ; let reject ; const p = new Promise < any > ( ( res , rej ) : void => { resolve = res ; reject = rej ; } ) ; return { promise : p , reject : reject ! , resolve : resolve ! , } ; } param ms return public static delay ( ms ) ; param value param ms return public static delay < R > ( value , ms ) ; param value param ms return public static delay < R > ( ... args ) { if ( arguments [ 1 ] === undefined ) { const ms = arguments [ 0 ] ; return new Promise < void | R > ( ( resolve ) => { setTimeout ( resolve , ms ) ; } ) ; } return Promise . resolve ( arguments [ 0 ] ) . delay ( arguments [ 1 ] ) ; } see onPossiblyUnhandledRejection param handler public static onUnhandledRejection ( handler ) { if ( handler === true ) { Promise . _onUnhandledRejectionHandler = defaultUnhandledRejectionHandler ; } else if ( handler === false ) { Promise . _onUnhandledRejectionHandler = noop ; } else if ( typeof handler !== "function" ) { throw new TypeError ( "invalid handler: boolean or function expected" ) ; } else { Promise . _onUnhandledRejectionHandler = handler ; } } see onUnhandledRejection see onPossiblyUnhandledRejectionHandled param handler public static onPossiblyUnhandledRejection ( handler ) { if ( handler === true ) { Promise . _onPossiblyUnhandledRejectionHandler = defaultPossiblyUnhandledRejectionHandler ; } else if ( handler === false ) { Promise . _onPossiblyUnhandledRejectionHandler = noop ; } else if ( typeof handler !== "function" ) { throw new TypeError ( "invalid handler: boolean or function expected" ) ; } else { Promise . _onPossiblyUnhandledRejectionHandler = handler ; } } see onPossiblyUnhandledRejection param handler public static onPossiblyUnhandledRejectionHandled ( handler ) { if ( handler === true ) { Promise . _onPossiblyUnhandledRejectionHandledHandler = defaultPossiblyUnhandledRejectionHandledHandler ; } else if ( handler === false ) { Promise . _onPossiblyUnhandledRejectionHandledHandler = noop ; } else if ( typeof handler !== "function" ) { throw new TypeError ( "invalid handler: boolean or function expected" ) ; } else { Promise . _onPossiblyUnhandledRejectionHandledHandler = handler ; } } param enable public static setLongTraces ( enable ) { longTraces = enable ; } param tracer public static setTracer ( tracer ) { if ( typeof tracer === "function" ) { trace = tracer ; } else { trace = undefined ; } } /**
     * Recursively flush the async callback queue until all `.then()` and
     * `.done()` callbacks for fulfilled and rejected Promises have been called.
     * Useful in e.g. unit tests to advance program state to the next 'tick'.
     *
     * Note that if e.g. `.done()` encounters a rejected promise, `flush()` will
     * immediately throw an error (e.g. `UnhandledRejectionError`).
     * It is safe to call `flush()` again afterwards, but it will also be called
     * automatically by the async queue on the next 'real' tick.
     *
     * It is an error to call `flush()` while it is already running (e.g. from
     * a `.then()` callback).
     */ public static flush ( ) { async . flush ( ) ; } param handler private static _unwrapper ( handler ) { handler . promise . _unwrap ( handler ) ; } param promise private static _unhandledRejectionChecker ( promise ) { promise . _doCheckUnhandledRejection ( ) ; } } Promise . onUnhandledRejection ( true ) ; Promise . onPossiblyUnhandledRejection ( true ) ; Promise . onPossiblyUnhandledRejectionHandled ( true ) ; export default Promise ;