Skip to content

Commit 0ddd1c7

Browse files
authoredJan 20, 2023
Added Promise.any polyfill + tests (#152)
* Added Promise.any polyfill + tests * Moved Promise.any into a separate module, added it to index.js & polyfill.js
1 parent e42be4e commit 0ddd1c7

File tree

6 files changed

+242
-2
lines changed

6 files changed

+242
-2
lines changed
 

‎dist/polyfill.js

+47
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,48 @@ function allSettled(arr) {
7474
});
7575
}
7676

77+
/**
78+
* @constructor
79+
*/
80+
function AggregateError(errors, message) {
81+
this.name = 'AggregateError', this.errors = errors;
82+
this.message = message || '';
83+
}
84+
AggregateError.prototype = Error.prototype;
85+
86+
function any(arr) {
87+
var P = this;
88+
return new P(function(resolve, reject) {
89+
if (!(arr && typeof arr.length !== 'undefined')) {
90+
return reject(new TypeError('Promise.any accepts an array'));
91+
}
92+
93+
var args = Array.prototype.slice.call(arr);
94+
if (args.length === 0) return reject();
95+
96+
var rejectionReasons = [];
97+
for (var i = 0; i < args.length; i++) {
98+
try {
99+
P.resolve(args[i])
100+
.then(resolve)
101+
.catch(function(error) {
102+
rejectionReasons.push(error);
103+
if (rejectionReasons.length === args.length) {
104+
reject(
105+
new AggregateError(
106+
rejectionReasons,
107+
'All promises were rejected'
108+
)
109+
);
110+
}
111+
});
112+
} catch (ex) {
113+
reject(ex);
114+
}
115+
}
116+
});
117+
}
118+
77119
// Store setTimeout reference so promise-polyfill will be unaffected by
78120
// other code modifying setTimeout (like sinon.useFakeTimers())
79121
var setTimeoutFunc = setTimeout;
@@ -277,6 +319,8 @@ Promise.all = function(arr) {
277319
});
278320
};
279321

322+
Promise.any = any;
323+
280324
Promise.allSettled = allSettled;
281325

282326
Promise.resolve = function(value) {
@@ -355,6 +399,9 @@ if (typeof globalNS['Promise'] !== 'function') {
355399
if (!globalNS.Promise.allSettled) {
356400
globalNS.Promise.allSettled = allSettled;
357401
}
402+
if (!globalNS.Promise.any) {
403+
globalNS.Promise.any = any;
404+
}
358405
}
359406

360407
})));

‎dist/polyfill.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/any.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @constructor
3+
*/
4+
function AggregateError(errors, message) {
5+
(this.name = 'AggregateError'), (this.errors = errors);
6+
this.message = message || '';
7+
}
8+
AggregateError.prototype = Error.prototype;
9+
10+
function any(arr) {
11+
var P = this;
12+
return new P(function(resolve, reject) {
13+
if (!(arr && typeof arr.length !== 'undefined')) {
14+
return reject(new TypeError('Promise.any accepts an array'));
15+
}
16+
17+
var args = Array.prototype.slice.call(arr);
18+
if (args.length === 0) return reject();
19+
20+
var rejectionReasons = [];
21+
for (var i = 0; i < args.length; i++) {
22+
try {
23+
P.resolve(args[i])
24+
.then(resolve)
25+
.catch(function(error) {
26+
rejectionReasons.push(error);
27+
if (rejectionReasons.length === args.length) {
28+
reject(
29+
new AggregateError(
30+
rejectionReasons,
31+
'All promises were rejected'
32+
)
33+
);
34+
}
35+
});
36+
} catch (ex) {
37+
reject(ex);
38+
}
39+
}
40+
});
41+
}
42+
43+
export default any;

‎src/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import promiseFinally from './finally';
22
import allSettled from './allSettled';
3+
import any from './any';
34

45
// Store setTimeout reference so promise-polyfill will be unaffected by
56
// other code modifying setTimeout (like sinon.useFakeTimers())
@@ -204,6 +205,8 @@ Promise.all = function(arr) {
204205
});
205206
};
206207

208+
Promise.any = any;
209+
207210
Promise.allSettled = allSettled;
208211

209212
Promise.resolve = function(value) {

‎src/polyfill.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Promise from './index';
22
import promiseFinally from './finally';
33
import allSettled from './allSettled';
4+
import any from './any';
45

56
/** @suppress {undefinedVars} */
67
var globalNS = (function() {
@@ -28,8 +29,11 @@ if (typeof globalNS['Promise'] !== 'function') {
2829
} else {
2930
if (!globalNS.Promise.prototype['finally']) {
3031
globalNS.Promise.prototype['finally'] = promiseFinally;
31-
}
32+
}
3233
if (!globalNS.Promise.allSettled) {
3334
globalNS.Promise.allSettled = allSettled;
3435
}
36+
if (!globalNS.Promise.any) {
37+
globalNS.Promise.any = any;
38+
}
3539
}

‎test/promise.js

+143
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,149 @@ describe('Promise', function() {
395395
});
396396
});
397397

398+
describe('Promise.any', function() {
399+
it('throws on implicit undefined', function() {
400+
return Promise.any().then(
401+
function() {
402+
assert.fail();
403+
},
404+
function(error) {
405+
assert.ok(error instanceof Error);
406+
}
407+
);
408+
});
409+
it('throws on explicit undefined', function() {
410+
return Promise.all(undefined).then(
411+
function() {
412+
assert.fail();
413+
},
414+
function(error) {
415+
assert.ok(error instanceof Error);
416+
}
417+
);
418+
});
419+
it('throws on null', function() {
420+
return Promise.all(null).then(
421+
function() {
422+
assert.fail();
423+
},
424+
function(error) {
425+
assert.ok(error instanceof Error);
426+
}
427+
);
428+
});
429+
it('works on array with a single resolved promise', function() {
430+
return Promise.any([Promise.resolve()]).then(
431+
function() {
432+
assert.ok(true);
433+
},
434+
function() {
435+
assert.fail();
436+
}
437+
);
438+
});
439+
it('works on array with multiple resolved promises', function() {
440+
return Promise.any([
441+
Promise.resolve('first'),
442+
Promise.resolve('second')
443+
]).then(
444+
function(value) {
445+
assert.equal(value, 'first');
446+
},
447+
function() {
448+
assert.fail();
449+
}
450+
);
451+
});
452+
it('works on array with at least one resolved promise', function() {
453+
return Promise.any([
454+
Promise.reject(),
455+
Promise.resolve('second'),
456+
Promise.reject(),
457+
Promise.resolve('fourth')
458+
]).then(
459+
function(value) {
460+
assert.equal(value, 'second');
461+
},
462+
function() {
463+
assert.fail();
464+
}
465+
);
466+
});
467+
it('works on array with a single rejected promise', function() {
468+
return Promise.any([Promise.reject('error')]).then(
469+
function() {
470+
assert.fail();
471+
},
472+
function(aggregateError) {
473+
assert.ok(aggregateError instanceof Error);
474+
assert.equal(aggregateError.name, 'AggregateError');
475+
assert.equal(aggregateError.errors.length, 1);
476+
assert.equal(aggregateError.errors[0], 'error');
477+
}
478+
);
479+
});
480+
it('works on array with multiple rejected promises', function() {
481+
return Promise.any([
482+
Promise.reject('first'),
483+
Promise.reject('second')
484+
]).then(
485+
function() {
486+
assert.fail();
487+
},
488+
function(aggregateError) {
489+
assert.ok(aggregateError instanceof Error);
490+
assert.equal(aggregateError.name, 'AggregateError');
491+
assert.equal(aggregateError.errors.length, 2);
492+
assert.equal(aggregateError.errors[0], 'first');
493+
assert.equal(aggregateError.errors[1], 'second');
494+
}
495+
);
496+
});
497+
it('works on array without promises', function() {
498+
return Promise.any([1, 'value', {}]).then(
499+
function(value) {
500+
assert.equal(value, 1);
501+
},
502+
function() {
503+
assert.fail();
504+
}
505+
);
506+
});
507+
it('works on array with asynchronously resolved promises', function(done) {
508+
Promise.any([
509+
new Promise(function(resolve) {
510+
setTimeout(function() {
511+
return resolve('first (slow)');
512+
}, 100);
513+
}),
514+
new Promise(function(resolve) {
515+
setTimeout(function() {
516+
return resolve('second (fast)');
517+
}, 50);
518+
})
519+
]).then(
520+
function(value) {
521+
assert.equal(value, 'second (fast)');
522+
done();
523+
},
524+
function() {
525+
assert.fail();
526+
}
527+
);
528+
});
529+
it('works on empty array', function() {
530+
return Promise.any([]).then(
531+
function() {
532+
assert.fail();
533+
},
534+
function() {
535+
assert.ok(true);
536+
}
537+
);
538+
});
539+
});
540+
398541
describe('Promise.allSettled', function() {
399542
it('throws on implicit undefined', function() {
400543
return Promise.allSettled().then(

0 commit comments

Comments
 (0)
Please sign in to comment.