Skip to content

Commit 4d494d2

Browse files
JiaLiPassionthePunderWoman
authored andcommitted
feat(zone.js): add Promise.any() implementation (#45064)
Implements `Promise.any()` introduced in ES2021. Close #44393 PR Close #45064
1 parent f4fd488 commit 4d494d2

5 files changed

Lines changed: 171 additions & 12 deletions

File tree

goldens/size-tracking/aio-payloads.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"uncompressed": {
1717
"runtime": 4343,
1818
"main": 450179,
19-
"polyfills": 37297,
19+
"polyfills": 37823,
2020
"styles": 70416,
2121
"light-theme": 77582,
2222
"dark-theme": 77711

goldens/size-tracking/integration-payloads.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"uncompressed": {
55
"runtime": 1083,
66
"main": 126218,
7-
"polyfills": 37226
7+
"polyfills": 37778
88
}
99
}
1010
},
@@ -16,7 +16,7 @@
1616
"main": "Likely there is a missing PURE annotation https://github.com/angular/angular/pull/43344",
1717
"main": "Tracking issue: https://github.com/angular/angular/issues/43568",
1818
"main": 20378,
19-
"polyfills": 37250
19+
"polyfills": 37802
2020
}
2121
}
2222
},
@@ -25,7 +25,7 @@
2525
"uncompressed": {
2626
"runtime": 1105,
2727
"main": 131882,
28-
"polyfills": 37248
28+
"polyfills": 37800
2929
}
3030
}
3131
},
@@ -34,7 +34,7 @@
3434
"uncompressed": {
3535
"runtime": 929,
3636
"main": 124544,
37-
"polyfills": 37933
37+
"polyfills": 38488
3838
}
3939
}
4040
},
@@ -43,7 +43,7 @@
4343
"uncompressed": {
4444
"runtime": 2835,
4545
"main": 233348,
46-
"polyfills": 37244,
46+
"polyfills": 37796,
4747
"src_app_lazy_lazy_module_ts": 795
4848
}
4949
}
@@ -53,7 +53,7 @@
5353
"uncompressed": {
5454
"runtime": 1063,
5555
"main": 158556,
56-
"polyfills": 36975
56+
"polyfills": 37758
5757
}
5858
}
5959
},
@@ -62,7 +62,7 @@
6262
"uncompressed": {
6363
"runtime": 1070,
6464
"main": 158300,
65-
"polyfills": 37242
65+
"polyfills": 37768
6666
}
6767
}
6868
},
@@ -77,4 +77,4 @@
7777
}
7878
}
7979
}
80-
}
80+
}

packages/zone.js/lib/common/promise.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
254254
const promiseState = (promise as any)[symbolState];
255255
const delegate = promiseState ?
256256
(typeof onFulfilled === 'function') ? onFulfilled : forwardResolution :
257-
(typeof onRejected === 'function') ? onRejected : forwardRejection;
257+
(typeof onRejected === 'function') ? onRejected :
258+
forwardRejection;
258259
zone.scheduleMicroTask(source, () => {
259260
try {
260261
const parentPromiseValue = (promise as any)[symbolValue];
@@ -283,6 +284,8 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
283284

284285
const noop = function() {};
285286

287+
const AggregateError = global.AggregateError;
288+
286289
class ZoneAwarePromise<R> implements Promise<R> {
287290
static toString() {
288291
return ZONE_AWARE_PROMISE_TO_STRING;
@@ -296,6 +299,47 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
296299
return resolvePromise(<ZoneAwarePromise<U>>new this(null as any), REJECTED, error);
297300
}
298301

302+
static any<T>(values: Iterable<PromiseLike<T>>): Promise<T> {
303+
if (!values || typeof values[Symbol.iterator] !== 'function') {
304+
return Promise.reject(new AggregateError([], 'All promises were rejected'));
305+
}
306+
const promises: Promise<PromiseLike<T>>[] = [];
307+
let count = 0;
308+
try {
309+
for (let v of values) {
310+
count++;
311+
promises.push(ZoneAwarePromise.resolve(v));
312+
}
313+
} catch (err) {
314+
return Promise.reject(new AggregateError([], 'All promises were rejected'));
315+
}
316+
if (count === 0) {
317+
return Promise.reject(new AggregateError([], 'All promises were rejected'));
318+
}
319+
let finished = false;
320+
const errors: any[] = [];
321+
return new ZoneAwarePromise((resolve, reject) => {
322+
for (let i = 0; i < promises.length; i++) {
323+
promises[i].then(
324+
v => {
325+
if (finished) {
326+
return;
327+
}
328+
finished = true;
329+
resolve(v);
330+
},
331+
err => {
332+
errors.push(err);
333+
count--;
334+
if (count === 0) {
335+
finished = true;
336+
reject(new AggregateError(errors, 'All promises were rejected'));
337+
}
338+
});
339+
}
340+
});
341+
};
342+
299343
static race<R>(values: PromiseLike<any>[]): Promise<R> {
300344
let resolve: (v: any) => void;
301345
let reject: (v: any) => void;

packages/zone.js/test/common/Promise.spec.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,121 @@ describe(
599599
});
600600
});
601601

602+
describe('Promise.any', () => {
603+
const any = (Promise as any).any;
604+
it('undefined parameters', (done: DoneFn) => {
605+
any().then(
606+
() => {
607+
fail('should not get a resolved promise.');
608+
},
609+
(err: any) => {
610+
expect(err.message).toEqual('All promises were rejected');
611+
expect(err.errors).toEqual([]);
612+
done();
613+
});
614+
});
615+
it('invalid iterable', (done: DoneFn) => {
616+
const invalidIterable: any = {};
617+
invalidIterable[Symbol.iterator] = () => 2;
618+
any(invalidIterable)
619+
.then(
620+
() => {
621+
fail('should not get a resolved promise.');
622+
},
623+
(err: any) => {
624+
expect(err.message).toEqual('All promises were rejected');
625+
expect(err.errors).toEqual([]);
626+
done();
627+
});
628+
});
629+
it('empty parameters', (done: DoneFn) => {
630+
any([]).then(
631+
() => {
632+
fail('should not get a resolved promise.');
633+
},
634+
(err: any) => {
635+
expect(err.message).toEqual('All promises were rejected');
636+
expect(err.errors).toEqual([]);
637+
done();
638+
});
639+
});
640+
it('non promises parameters', (done: DoneFn) => {
641+
any([1, 'test'])
642+
.then(
643+
(v: any) => {
644+
expect(v).toBe(1);
645+
done();
646+
},
647+
(err: any) => {
648+
fail('should not get a rejected promise.');
649+
});
650+
});
651+
it('mixed parameters, non promise first', (done: DoneFn) => {
652+
any([1, Promise.resolve(2)])
653+
.then(
654+
(v: any) => {
655+
expect(v).toBe(1);
656+
done();
657+
},
658+
(err: any) => {
659+
fail('should not get a rejected promise.');
660+
});
661+
});
662+
it('mixed parameters, promise first', (done: DoneFn) => {
663+
any([Promise.resolve(1), 2])
664+
.then(
665+
(v: any) => {
666+
expect(v).toBe(1);
667+
done();
668+
},
669+
(err: any) => {
670+
fail('should not get a rejected promise.');
671+
});
672+
});
673+
it('all ok promises', (done: DoneFn) => {
674+
any([Promise.resolve(1), Promise.resolve(2)])
675+
.then(
676+
(v: any) => {
677+
expect(v).toBe(1);
678+
done();
679+
},
680+
(err: any) => {
681+
fail('should not get a rejected promise.');
682+
});
683+
});
684+
it('all promises, first rejected', (done: DoneFn) => {
685+
any([Promise.reject('error'), Promise.resolve(2)])
686+
.then(
687+
(v: any) => {
688+
expect(v).toBe(2);
689+
done();
690+
},
691+
(err: any) => {
692+
fail('should not get a rejected promise.');
693+
});
694+
});
695+
it('all promises, second rejected', (done: DoneFn) => {
696+
any([Promise.resolve(1), Promise.reject('error')])
697+
.then(
698+
(v: any) => {
699+
expect(v).toBe(1);
700+
done();
701+
},
702+
(err: any) => {
703+
fail('should not get a rejected promise.');
704+
});
705+
});
706+
it('all rejected promises', (done: DoneFn) => {
707+
any([
708+
Promise.reject('error1'), Promise.reject('error2')
709+
]).then((v: any) => {fail('should not get a resolved promise.')}, (err: any) => {
710+
expect(err.message).toEqual('All promises were rejected');
711+
expect(err.errors).toEqual(['error1', 'error2']);
712+
done();
713+
});
714+
});
715+
});
716+
602717
describe('Promise.allSettled', () => {
603718
const yes = function makeFulfilledResult(value: any) {
604719
return {status: 'fulfilled', value: value};

packages/zone.js/tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"compilerOptions": {
33
"module": "commonjs",
4-
"target": "es5",
4+
"target": "es2015",
55
"outDir": "build",
66
"inlineSourceMap": true,
77
"inlineSources": true,
@@ -38,4 +38,4 @@
3838
"test/node_error_entry_point.ts",
3939
"test/node_tests.ts"
4040
]
41-
}
41+
}

0 commit comments

Comments
 (0)