Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Promise.any() #174

Merged
merged 1 commit into from
Oct 25, 2022
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wandering is it correct way of polyfilling this AggregateError

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be "good enough" as a polyfill.


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