Skip to content
This repository has been archived by the owner on Jul 30, 2018. It is now read-only.

Forward compatible updates for TS 2.4 #342

Merged
merged 3 commits into from Jun 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
134 changes: 99 additions & 35 deletions src/async/ExtensiblePromise.ts
Expand Up @@ -10,7 +10,7 @@ import '@dojo/shim/Symbol';
* @param iterable The list of objects to iterate over
* @returns {any[]} The list of objects, as an array, with ExtensiblePromises being replaced by Promises.
*/
function unwrapPromises(iterable: Iterable<any> | any[]): any[] {
export function unwrapPromises(iterable: Iterable<any> | any[]): any[] {
const unwrapped: any[] = [];
forOf(iterable, function (item: any): void {
unwrapped.push(item instanceof ExtensiblePromise ? item._promise : item);
Expand All @@ -29,24 +29,40 @@ export default class ExtensiblePromise<T> {
/**
* Return a rejected promise wrapped in an ExtensiblePromise
*
* @param {Error?} reason The reason for the rejection
* @returns {ExtensiblePromise}
* @param reason The reason for the rejection
* @returns An extensible promise
*/
static reject<T>(reason?: any): ExtensiblePromise<never>;

/**
* Return a rejected promise wrapped in an ExtensiblePromise
*
* @param reason The reason for the rejection
* @returns An extensible promise
*/
static reject<T>(reason?: Error): any {
return new this<T>((resolve, reject) => reject(reason));
static reject<T>(reason?: any): ExtensiblePromise<T> {
return new this((resolve, reject) => reject(reason));
}

/**
* Return a resolved promise wrapped in an ExtensiblePromise
*
* @param value The value to resolve the promise with
*
* @returns {ExtensiblePromise}
* @returns An extensible promise
*/
static resolve<F extends ExtensiblePromise<void>>(): F;
static resolve<T, F extends ExtensiblePromise<T>>(value: (T | Thenable<T>)): F;
static resolve<T, F extends ExtensiblePromise<T>>(value?: any): F {
return <F> new this<T>((resolve, reject) => resolve(value));
static resolve<P extends ExtensiblePromise<void>>(): P;

/**
* Return a resolved promise wrapped in an ExtensiblePromise
*
* @param value The value to resolve the promise with
*
* @returns An extensible promise
*/
static resolve<T, P extends ExtensiblePromise<T>>(value: T | PromiseLike<T>): P;
static resolve(value?: any | PromiseLike<any>): ExtensiblePromise<any> {
return new this((resolve, reject) => resolve(value));
}

/**
Expand All @@ -58,30 +74,78 @@ export default class ExtensiblePromise<T> {
* // { one: 1, two: 2 }
*
* @param iterable An iterable of values to resolve, or a key/value pair of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns {ExtensiblePromise}
* @returns An extensible promise
*/
static all<T>(iterable: DictionaryOfPromises<T>): ExtensiblePromise<{ [key: string]: T }>;

/**
* Return a ExtensiblePromise that resolves when all of the passed in objects have resolved. When used with a key/value
* pair, the returned promise's argument is a key/value pair of the original keys with their resolved values.
*
* @example
* ExtensiblePromise.all({ one: 1, two: 2 }).then(results => console.log(results));
* // { one: 1, two: 2 }
*
* @param iterable An iterable of values to resolve, or a key/value pair of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns An extensible promise
*/
static all<T>(iterable: (T | Thenable<T>)[]): ExtensiblePromise<T[]>;

/**
* Return a ExtensiblePromise that resolves when all of the passed in objects have resolved. When used with a key/value
* pair, the returned promise's argument is a key/value pair of the original keys with their resolved values.
*
* @example
* ExtensiblePromise.all({ one: 1, two: 2 }).then(results => console.log(results));
* // { one: 1, two: 2 }
*
* @param iterable An iterable of values to resolve, or a key/value pair of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns An extensible promise
*/
static all<T>(iterable: T | Thenable<T>): ExtensiblePromise<T[]>;

/**
* Return a ExtensiblePromise that resolves when all of the passed in objects have resolved. When used with a key/value
* pair, the returned promise's argument is a key/value pair of the original keys with their resolved values.
*
* @example
* ExtensiblePromise.all({ one: 1, two: 2 }).then(results => console.log(results));
* // { one: 1, two: 2 }
*
* @param iterable An iterable of values to resolve, or a key/value pair of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns An extensible promise
*/
static all<T>(iterable: ListOfPromises<T>): ExtensiblePromise<T[]>;

/**
* Return a ExtensiblePromise that resolves when all of the passed in objects have resolved. When used with a key/value
* pair, the returned promise's argument is a key/value pair of the original keys with their resolved values.
*
* @example
* ExtensiblePromise.all({ one: 1, two: 2 }).then(results => console.log(results));
* // { one: 1, two: 2 }
*
* @param iterable An iterable of values to resolve, or a key/value pair of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns An extensible promise
*/
static all<F extends ExtensiblePromise<{ [key: string]: T }>, T>(iterable: DictionaryOfPromises<T>): F;
static all<F extends ExtensiblePromise<T[]>, T>(iterable: (T | Thenable<T>)[]): F;
static all<F extends ExtensiblePromise<T[]>, T>(iterable: T | Thenable<T>): F;
static all<F extends ExtensiblePromise<T[]>, T>(iterable: ListOfPromises<T>): F;
static all<F extends ExtensiblePromise<any>, T>(iterable: DictionaryOfPromises<T> | ListOfPromises<T>): F {
static all<T>(iterable: DictionaryOfPromises<T> | ListOfPromises<T>): ExtensiblePromise<T[] | { [key: string]: T }> {
if (!isArrayLike(iterable) && !isIterable(iterable)) {
const promiseKeys = Object.keys(iterable);

return <F> new this((resolve, reject) => {
Promise.all(promiseKeys.map(key => (<DictionaryOfPromises<T>> iterable)[ key ])).then((promiseResults: T[]) => {
const returnValue: {[_: string]: T} = {};
return new this((resolve, reject) => {
Promise.all(promiseKeys.map(key => iterable[key])).then((promiseResults: T[]) => {
const returnValue: { [key: string]: T} = {};

promiseResults.forEach((value: T, index: number) => {
returnValue[ promiseKeys[ index ] ] = value;
returnValue[promiseKeys[index]] = value;
});

resolve(returnValue);
}, reject);
});
}

return <F> new this((resolve, reject) => {
return new this((resolve, reject) => {
Promise.all(unwrapPromises(<Iterable<T>> iterable)).then(resolve, reject);
});
}
Expand All @@ -92,8 +156,8 @@ export default class ExtensiblePromise<T> {
* @param iterable An iterable of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns {ExtensiblePromise}
*/
static race<F extends ExtensiblePromise<T>, T>(iterable: Iterable<(T | Thenable<T>)> | (T | Thenable<T>)[]): F {
return <F> new this((resolve, reject) => {
static race<T>(iterable: Iterable<(T | PromiseLike<T>)> | (T | PromiseLike<T>)[]): ExtensiblePromise<T> {
return new this((resolve, reject) => {
Promise.race(unwrapPromises(iterable)).then(resolve, reject);
});
}
Expand Down Expand Up @@ -127,9 +191,8 @@ export default class ExtensiblePromise<T> {
*
* @returns {ExtensiblePromise}
*/
catch(onRejected: (reason: Error) => T | Thenable<T> | void): ExtensiblePromise<T>;
catch<U>(onRejected: (reason: Error) => U | Thenable<U>): ExtensiblePromise<U> {
return this.then<U>(undefined, onRejected);
catch<TResult = never>(onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): ExtensiblePromise<T | TResult> {
return this.then(undefined, onRejected);
}

/**
Expand All @@ -140,16 +203,17 @@ export default class ExtensiblePromise<T> {
*
* @returns {ExtensiblePromise}
*/
then<U, V>(onFulfilled: ((value: T) => (U | Thenable<U> | undefined)) | undefined, onRejected: (reason: Error) => (V | Thenable<V>)): ExtensiblePromise<U | V>;
then<U>(onFulfilled?: ((value: T) => (U | Thenable<U> | undefined)) | undefined, onRejected?: (reason: Error) => void): ExtensiblePromise<U>;
then<U>(onFulfilled?: ((value: T) => (U | Thenable<U> | undefined)) | undefined, onRejected?: (reason: Error) => (U | Thenable<U>)): ExtensiblePromise<U> {
const e: Executor<U> = (resolve, reject) => {
function handler(rejected: boolean, valueOrError: T | U | Error) {
const callback: ((value: T | U | Error) => (U | Thenable<U> | void)) | undefined = rejected ? onRejected : onFulfilled;
then<TResult1 = T, TResult2 = never>(
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2> | void) | undefined | null
): ExtensiblePromise<TResult1 | TResult2> {
const executor: Executor<TResult1> = (resolve, reject) => {
function handler(rejected: boolean, valueOrError: T | TResult1 | Error) {
const callback: ((value: any) => any) | null | undefined = rejected ? onRejected : onFulfilled;

if (typeof callback === 'function') {
try {
resolve(<U> callback(<T> valueOrError));
resolve(callback(valueOrError));
}
catch (error) {
reject(error);
Expand All @@ -159,14 +223,14 @@ export default class ExtensiblePromise<T> {
reject(valueOrError);
}
else {
resolve(<U> valueOrError);
resolve(valueOrError as TResult1);
}
}

this._promise.then(handler.bind(null, false), handler.bind(null, true));
};

return new (<{ new(executor: Executor<U>): any }> this.constructor)(e);
return new (this.constructor as typeof ExtensiblePromise)(executor);
}

readonly [Symbol.toStringTag]: 'Promise';
Expand Down
113 changes: 93 additions & 20 deletions src/async/Task.ts
@@ -1,6 +1,6 @@
import { Thenable } from '@dojo/shim/interfaces';
import { Executor } from '@dojo/shim/Promise';
import ExtensiblePromise, { ListOfPromises, DictionaryOfPromises } from './ExtensiblePromise';
import ExtensiblePromise, { ListOfPromises, DictionaryOfPromises, unwrapPromises } from './ExtensiblePromise';
import { Iterable } from '@dojo/shim/iterator';

/**
Expand Down Expand Up @@ -40,39 +40,116 @@ export default class Task<T> extends ExtensiblePromise<T> {
* @param iterable An iterable of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns {Task}
*/
static race<F extends ExtensiblePromise<T>, T>(iterable: Iterable<(T | Thenable<T>)> | (T | Thenable<T>)[]): Task<T> {
return <Task<T>> super.race(iterable);
static race<T>(iterable: Iterable<(T | Thenable<T>)> | (T | Thenable<T>)[]): Task<T> {
return new this((resolve, reject) => {
Promise.race(unwrapPromises(iterable)).then(resolve, reject);
});
}

/**
* Return a rejected promise wrapped in a Task
*
* @param {Error?} reason The reason for the rejection
* @returns {Task}
* @param reason The reason for the rejection
* @returns A task
*/
static reject<T>(reason?: Error): Task<T> {
return new this<T>((resolve, reject) => reject(reason));
return new this((resolve, reject) => reject(reason));
}

/**
* Return a resolved task.
*
* @param value The value to resolve with
*
* @return {Task}
* @return A task
*/
public static resolve(): Task<void>;

/**
* Return a resolved task.
*
* @param value The value to resolve with
*
* @return A task
*/
public static resolve<T>(value: (T | Thenable<T>)): Task<T>;

/**
* Return a resolved task.
*
* @param value The value to resolve with
*
* @return A task
*/
public static resolve<T>(value?: any): Task<T> {
return new this<T>((resolve, reject) => resolve(value));
}

/**
* Return a ExtensiblePromise that resolves when all of the passed in objects have resolved. When used with a key/value
* pair, the returned promise's argument is a key/value pair of the original keys with their resolved values.
*
* @example
* ExtensiblePromise.all({ one: 1, two: 2 }).then(results => console.log(results));
* // { one: 1, two: 2 }
*
* @param iterable An iterable of values to resolve, or a key/value pair of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns An extensible promise
*/
static all<T>(iterable: DictionaryOfPromises<T>): Task<{ [key: string]: T }>;

/**
* Return a ExtensiblePromise that resolves when all of the passed in objects have resolved. When used with a key/value
* pair, the returned promise's argument is a key/value pair of the original keys with their resolved values.
*
* @example
* ExtensiblePromise.all({ one: 1, two: 2 }).then(results => console.log(results));
* // { one: 1, two: 2 }
*
* @param iterable An iterable of values to resolve, or a key/value pair of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns An extensible promise
*/
static all<T>(iterable: (T | Thenable<T>)[]): Task<T[]>;

/**
* Return a ExtensiblePromise that resolves when all of the passed in objects have resolved. When used with a key/value
* pair, the returned promise's argument is a key/value pair of the original keys with their resolved values.
*
* @example
* ExtensiblePromise.all({ one: 1, two: 2 }).then(results => console.log(results));
* // { one: 1, two: 2 }
*
* @param iterable An iterable of values to resolve, or a key/value pair of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns An extensible promise
*/
static all<T>(iterable: T | Thenable<T>): Task<T[]>;

/**
* Return a ExtensiblePromise that resolves when all of the passed in objects have resolved. When used with a key/value
* pair, the returned promise's argument is a key/value pair of the original keys with their resolved values.
*
* @example
* ExtensiblePromise.all({ one: 1, two: 2 }).then(results => console.log(results));
* // { one: 1, two: 2 }
*
* @param iterable An iterable of values to resolve, or a key/value pair of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns An extensible promise
*/
static all<T>(iterable: ListOfPromises<T>): Task<T[]>;

/**
* Return a ExtensiblePromise that resolves when all of the passed in objects have resolved. When used with a key/value
* pair, the returned promise's argument is a key/value pair of the original keys with their resolved values.
*
* @example
* ExtensiblePromise.all({ one: 1, two: 2 }).then(results => console.log(results));
* // { one: 1, two: 2 }
*
* @param iterable An iterable of values to resolve, or a key/value pair of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @returns An extensible promise
*/
static all<T>(iterable: DictionaryOfPromises<T> | ListOfPromises<T>): Task<any> {
return <Task<T>> super.all(iterable);
return super.all(iterable) as Task<any>;
}

/**
Expand Down Expand Up @@ -195,9 +272,8 @@ export default class Task<T> extends ExtensiblePromise<T> {
}
}

catch(onRejected: (reason: Error) => T | Thenable<T> | void): Task<T>;
catch<U>(onRejected: (reason: Error) => U | Thenable<U>): Task<U> {
return this.then(undefined, onRejected);
catch<TResult = never>(onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined): Task<T | TResult> {
return this.then(undefined, onRejected) as Task<T | TResult>;
}

/**
Expand Down Expand Up @@ -225,18 +301,15 @@ export default class Task<T> extends ExtensiblePromise<T> {
/**
* Adds a callback to be invoked when the Task resolves or is rejected.
*
* @param {Function} onFulfilled A function to call to handle the resolution. The paramter to the function will be the resolved value, if any.
* @param {Function} onRejected A function to call to handle the error. The parameter to the function will be the caught error.
* @param onFulfilled A function to call to handle the resolution. The paramter to the function will be the resolved value, if any.
* @param onRejected A function to call to handle the error. The parameter to the function will be the caught error.
*
* @returns {ExtensiblePromise}
* @returns A task
*/
then<U, V>(onFulfilled: ((value: T) => (U | Thenable<U> | undefined)) | undefined, onRejected: (reason: Error) => (V | Thenable<V>)): Task<U | V>;
then<U>(onFulfilled?: ((value: T) => (U | Thenable<U> | undefined)) | undefined, onRejected?: (reason: Error) => void): Task<U>;
then<U>(onFulfilled?: ((value: T) => (U | Thenable<U> | undefined)) | undefined, onRejected?: (reason: Error) => (U | Thenable<U>)): Task<U>;
then<U>(onFulfilled?: (value?: T) => U | Thenable<U>, onRejected?: (error: Error) => U | Thenable<U>): Task<U> {
then<TResult1 = T, TResult2 = never>(onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Task<TResult1 | TResult2> {
// FIXME
// tslint:disable-next-line:no-var-keyword
var task = <Task<U>> super.then<U>(
var task = super.then(
// Don't call the onFulfilled or onRejected handlers if this Task is canceled
function (value) {
if (task._state === State.Canceled) {
Expand All @@ -256,7 +329,7 @@ export default class Task<T> extends ExtensiblePromise<T> {
}
throw error;
}
);
) as Task<TResult1 | TResult2>;

task.canceler = () => {
// If task's parent (this) hasn't been resolved, cancel it; downward propagation will start at the first
Expand Down