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

Commit

Permalink
Adding object support to ExtensiblePromise.all, issue #211 (#241)
Browse files Browse the repository at this point in the history
* Adding object support to ExtensiblePromise.all, issue #211
* Code review feedback
* Cleaning up function definition of all, issue #211
  • Loading branch information
rorticus authored Dec 13, 2016
1 parent 1d661a2 commit d6b5820
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 6 deletions.
38 changes: 32 additions & 6 deletions src/async/ExtensiblePromise.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Iterable, forOf } from 'dojo-shim/iterator';
import { Iterable, forOf, isIterable, isArrayLike } from 'dojo-shim/iterator';
import Promise, { Executor } from 'dojo-shim/Promise';
import { Thenable } from 'dojo-shim/interfaces';

Expand All @@ -17,6 +17,9 @@ function unwrapPromises(iterable: Iterable<any> | any[]): any[] {
return unwrapped;
}

export type DictionaryOfPromises<T> = { [_: string]: T | Promise<T> | Thenable<T> };
export type ListOfPromises<T> = Iterable<(T | Thenable<T>)> | (T | Thenable<T>);

/**
* An extensible base to allow Promises to be extended in ES5. This class basically wraps a native Promise object,
* giving an API like a native promise.
Expand Down Expand Up @@ -46,14 +49,37 @@ export default class ExtensiblePromise<T> {
}

/**
* Return a ExtensiblePromise that resolves when all of the passed in objects have resolved
* 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.
*
* @param iterable An iterable of values to resolve. These can be Promises, ExtensiblePromises, or other objects
* @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 {ExtensiblePromise}
*/
static all<F extends ExtensiblePromise<T>, T>(iterable: Iterable<(T | Thenable<T>)> | (T | Thenable<T>)[]): F {
static all<F extends ExtensiblePromise<{ [key: string]: T }>, T>(iterable: DictionaryOfPromises<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 {
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} = {};

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

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

return <F> new this((resolve, reject) => {
Promise.all(unwrapPromises(iterable)).then(resolve, reject);
Promise.all(unwrapPromises(<Iterable<T>> iterable)).then(resolve, reject);
});
}

Expand Down Expand Up @@ -113,7 +139,7 @@ export default class ExtensiblePromise<T> {
*/
then<U>(onFulfilled?: ((value: T) => (U | Thenable<U> | undefined)) | undefined, onRejected?: (reason: Error) => void): this;
then<U>(onFulfilled?: ((value: T) => (U | Thenable<U> | undefined)) | undefined, onRejected?: (reason: Error) => (U | Thenable<U>)): this {
let e: Executor<U> = (resolve, reject) => {
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;

Expand Down
33 changes: 33 additions & 0 deletions tests/unit/async/ExtensiblePromise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,38 @@ registerSuite({
assert.isTrue(false, 'Should not have resolved');
}), undefined).catch(dfd.callback(() => {
}));
},

'.all': {
'with array'() {
return ExtensiblePromise.all([ 1, ExtensiblePromise.resolve(2), new ExtensiblePromise((resolve) => {
setTimeout(() => resolve(3), 100);
}) ]).then((results: any) => {
assert.deepEqual(results, [ 1, 2, 3 ]);
});
},

'with object'() {
return ExtensiblePromise.all({
one: 1, two: ExtensiblePromise.resolve(2), three: new ExtensiblePromise((resolve) => {
setTimeout(() => resolve(3), 100);
})
}).then((results: any) => {
assert.deepEqual(results, { one: 1, two: 2, three: 3 });
});
},

'errors with object'(this: any) {
let dfd = this.async();

ExtensiblePromise.all({
one: ExtensiblePromise.resolve(1),
two: ExtensiblePromise.reject(new Error('error message'))
}).then(dfd.rejectOnError(() => {
assert.fail('should have failed');
}), dfd.callback((error: Error) => {
assert.equal(error.message, 'error message');
}));
}
}
});

0 comments on commit d6b5820

Please sign in to comment.