Skip to content

Commit 9d5851d

Browse files
authored
Implement Promise.any() (#174)
1 parent a4b9e3e commit 9d5851d

File tree

4 files changed

+143
-0
lines changed

4 files changed

+143
-0
lines changed

Readme.md

+14
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,20 @@ Promise.all([Promise.resolve('a'), 'b', Promise.resolve('c')])
141141
})
142142
```
143143

144+
#### Promise.any(array)
145+
146+
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`
147+
148+
```js
149+
var rejected = Promise.reject(0);
150+
var first = new Promise(function (resolve){ setTimeout(resolve, 100, 'quick') });
151+
var second = new Promise(function (resolve){ setTimeout(resolve, 500, 'slow') });
152+
153+
var promises = [rejected, first, second];
154+
155+
Promise.any(promises) // => succeeds with `quick`
156+
```
157+
144158
#### Promise.allSettled(array)
145159

146160
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.

index.d.ts

+15
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,21 @@ interface ThenPromiseConstructor {
6060
*/
6161
new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => any): ThenPromise<T>;
6262

63+
64+
/**
65+
* 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.
66+
* @param values An array or iterable of Promises.
67+
* @returns A new Promise.
68+
*/
69+
any<T extends readonly unknown[] | []>(values: T): Promise<Awaited<T[number]>>;
70+
71+
/**
72+
* 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.
73+
* @param values An array or iterable of Promises.
74+
* @returns A new Promise.
75+
*/
76+
any<T>(values: Iterable<T | PromiseLike<T>>): Promise<Awaited<T>>
77+
6378
/**
6479
* Creates a Promise that is resolved with an array of results when all
6580
* of the provided Promises resolve or reject.

src/es6-extensions.js

+44
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,47 @@ Promise.race = function (values) {
140140
Promise.prototype['catch'] = function (onRejected) {
141141
return this.then(null, onRejected);
142142
};
143+
144+
function getAggregateError(errors){
145+
if(typeof AggregateError === 'function'){
146+
return new AggregateError(errors,'All promises were rejected');
147+
}
148+
149+
var error = new Error('All promises were rejected');
150+
151+
error.name = 'AggregateError';
152+
error.errors = errors;
153+
154+
return error;
155+
}
156+
157+
Promise.any = function promiseAny(values) {
158+
return new Promise(function(resolve, reject) {
159+
var promises = iterableToArray(values);
160+
var hasResolved = false;
161+
var rejectionReasons = [];
162+
163+
function resolveOnce(value) {
164+
if (!hasResolved) {
165+
hasResolved = true;
166+
resolve(value);
167+
}
168+
}
169+
170+
function rejectionCheck(reason) {
171+
rejectionReasons.push(reason);
172+
173+
if (rejectionReasons.length === promises.length) {
174+
reject(getAggregateError(rejectionReasons));
175+
}
176+
}
177+
178+
if(promises.length === 0){
179+
reject(getAggregateError(rejectionReasons));
180+
} else {
181+
promises.forEach(function(value){
182+
Promise.resolve(value).then(resolveOnce, rejectionCheck);
183+
});
184+
}
185+
});
186+
};

test/extensions-tests.js

+70
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,76 @@ describe('extensions', function () {
130130
})
131131
})
132132
})
133+
describe('Promise.any(...)', function () {
134+
describe('an array', function () {
135+
describe('that is empty', function () {
136+
it('returns a rejected promise for an empty array', function (done) {
137+
var res = Promise.any([])
138+
assert(res instanceof Promise)
139+
res.catch(function (err) {
140+
assert(Array.isArray(err.errors))
141+
assert(err.errors.length === 0)
142+
}).nodeify(done)
143+
})
144+
it('returns a rejected promise for not argument', function (done) {
145+
var res = Promise.any()
146+
assert(res instanceof Promise)
147+
res.catch(function (err) {
148+
assert(err instanceof Error)
149+
}).nodeify(done)
150+
})
151+
})
152+
describe('of objects', function () {
153+
it('resolved with a first fulfilled value', function (done) {
154+
var res = Promise.any([a, b, c])
155+
assert(res instanceof Promise)
156+
res.then(function (res) {
157+
assert(a === res)
158+
}).nodeify(done)
159+
})
160+
})
161+
describe('of promises', function () {
162+
it('resolved with a first fulfilled value', function (done) {
163+
var res = Promise.any([B, C])
164+
assert(res instanceof Promise)
165+
res.then(function (res) {
166+
assert(b === res)
167+
}).nodeify(done)
168+
})
169+
})
170+
describe('of mixed values', function () {
171+
it('returns a promise for an array containing the fulfilled values', function (done) {
172+
var res = Promise.any([c,B])
173+
assert(res instanceof Promise)
174+
res.then(function (res) {
175+
assert(res === c)
176+
}).nodeify(done)
177+
})
178+
})
179+
describe('containing all rejected promise', function () {
180+
it('rejects the resulting promise', function (done) {
181+
var rejectionB ={test:2}
182+
var rejectedB = new Promise(function (resolve, reject) { reject(rejectionB) })
183+
var res = Promise.any([rejected, rejectedB])
184+
assert(res instanceof Promise)
185+
res.catch(function (err) {
186+
assert(Array.isArray(err.errors))
187+
assert(err.errors[0] === rejection)
188+
assert(err.errors[1] === rejectionB)
189+
assert(err.errors.length === 2)
190+
}).nodeify(done)
191+
})
192+
})
193+
describe('when given a foreign promise', function () {
194+
it('should provide the correct value of `this`', function (done) {
195+
var p = {then: function (onFulfilled) { onFulfilled({self: this}); }};
196+
Promise.any([p]).then(function (results) {
197+
assert(p === results.self);
198+
}).nodeify(done);
199+
});
200+
});
201+
})
202+
})
133203
describe('Promise.allSettled(...)', function () {
134204
describe('an array', function () {
135205
describe('that is empty', function () {

0 commit comments

Comments
 (0)