Skip to content

Commit

Permalink
Implement Promise.any() (#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
retyui committed Oct 25, 2022
1 parent a4b9e3e commit 9d5851d
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,20 @@ Promise.all([Promise.resolve('a'), 'b', Promise.resolve('c')])
})
```

#### Promise.any(array)

Returns a single promise that fulfills as soon as any of the promises in the iterable fulfills, with the value of the fulfilled promise. If no promises in the iterable fulfill (if all of the given promises are rejected), then the returned promise is rejected with an `AggregateError`

```js
var rejected = Promise.reject(0);
var first = new Promise(function (resolve){ setTimeout(resolve, 100, 'quick') });
var second = new Promise(function (resolve){ setTimeout(resolve, 500, 'slow') });

var promises = [rejected, first, second];

Promise.any(promises) // => succeeds with `quick`
```

#### Promise.allSettled(array)

Returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
Expand Down
15 changes: 15 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ interface ThenPromiseConstructor {
*/
new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => any): ThenPromise<T>;


/**
* The any function returns a promise that is fulfilled by the first given promise to be fulfilled, or rejected with an AggregateError containing an array of rejection reasons if all of the given promises are rejected. It resolves all elements of the passed iterable to promises as it runs this algorithm.
* @param values An array or iterable of Promises.
* @returns A new Promise.
*/
any<T extends readonly unknown[] | []>(values: T): Promise<Awaited<T[number]>>;

/**
* The any function returns a promise that is fulfilled by the first given promise to be fulfilled, or rejected with an AggregateError containing an array of rejection reasons if all of the given promises are rejected. It resolves all elements of the passed iterable to promises as it runs this algorithm.
* @param values An array or iterable of Promises.
* @returns A new Promise.
*/
any<T>(values: Iterable<T | PromiseLike<T>>): Promise<Awaited<T>>

/**
* Creates a Promise that is resolved with an array of results when all
* of the provided Promises resolve or reject.
Expand Down
44 changes: 44 additions & 0 deletions src/es6-extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,47 @@ Promise.race = function (values) {
Promise.prototype['catch'] = function (onRejected) {
return this.then(null, onRejected);
};

function getAggregateError(errors){
if(typeof AggregateError === 'function'){
return new AggregateError(errors,'All promises were rejected');
}

var error = new Error('All promises were rejected');

error.name = 'AggregateError';
error.errors = errors;

return error;
}

Promise.any = function promiseAny(values) {
return new Promise(function(resolve, reject) {
var promises = iterableToArray(values);
var hasResolved = false;
var rejectionReasons = [];

function resolveOnce(value) {
if (!hasResolved) {
hasResolved = true;
resolve(value);
}
}

function rejectionCheck(reason) {
rejectionReasons.push(reason);

if (rejectionReasons.length === promises.length) {
reject(getAggregateError(rejectionReasons));
}
}

if(promises.length === 0){
reject(getAggregateError(rejectionReasons));
} else {
promises.forEach(function(value){
Promise.resolve(value).then(resolveOnce, rejectionCheck);
});
}
});
};
70 changes: 70 additions & 0 deletions test/extensions-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,76 @@ describe('extensions', function () {
})
})
})
describe('Promise.any(...)', function () {
describe('an array', function () {
describe('that is empty', function () {
it('returns a rejected promise for an empty array', function (done) {
var res = Promise.any([])
assert(res instanceof Promise)
res.catch(function (err) {
assert(Array.isArray(err.errors))
assert(err.errors.length === 0)
}).nodeify(done)
})
it('returns a rejected promise for not argument', function (done) {
var res = Promise.any()
assert(res instanceof Promise)
res.catch(function (err) {
assert(err instanceof Error)
}).nodeify(done)
})
})
describe('of objects', function () {
it('resolved with a first fulfilled value', function (done) {
var res = Promise.any([a, b, c])
assert(res instanceof Promise)
res.then(function (res) {
assert(a === res)
}).nodeify(done)
})
})
describe('of promises', function () {
it('resolved with a first fulfilled value', function (done) {
var res = Promise.any([B, C])
assert(res instanceof Promise)
res.then(function (res) {
assert(b === res)
}).nodeify(done)
})
})
describe('of mixed values', function () {
it('returns a promise for an array containing the fulfilled values', function (done) {
var res = Promise.any([c,B])
assert(res instanceof Promise)
res.then(function (res) {
assert(res === c)
}).nodeify(done)
})
})
describe('containing all rejected promise', function () {
it('rejects the resulting promise', function (done) {
var rejectionB ={test:2}
var rejectedB = new Promise(function (resolve, reject) { reject(rejectionB) })
var res = Promise.any([rejected, rejectedB])
assert(res instanceof Promise)
res.catch(function (err) {
assert(Array.isArray(err.errors))
assert(err.errors[0] === rejection)
assert(err.errors[1] === rejectionB)
assert(err.errors.length === 2)
}).nodeify(done)
})
})
describe('when given a foreign promise', function () {
it('should provide the correct value of `this`', function (done) {
var p = {then: function (onFulfilled) { onFulfilled({self: this}); }};
Promise.any([p]).then(function (results) {
assert(p === results.self);
}).nodeify(done);
});
});
})
})
describe('Promise.allSettled(...)', function () {
describe('an array', function () {
describe('that is empty', function () {
Expand Down

0 comments on commit 9d5851d

Please sign in to comment.