Skip to content

Commit

Permalink
feat(zone.js): add Promise.any() implementation (angular#45064)
Browse files Browse the repository at this point in the history
Implements `Promise.any()` introduced in ES2021.

Close angular#44393

PR Close angular#45064
  • Loading branch information
JiaLiPassion authored and josmar-crwdstffng committed Apr 8, 2022
1 parent f37099c commit f223349
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 12 deletions.
2 changes: 1 addition & 1 deletion goldens/size-tracking/aio-payloads.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"uncompressed": {
"runtime": 4343,
"main": 450179,
"polyfills": 37297,
"polyfills": 37823,
"styles": 70416,
"light-theme": 77582,
"dark-theme": 77711
Expand Down
16 changes: 8 additions & 8 deletions goldens/size-tracking/integration-payloads.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"uncompressed": {
"runtime": 1083,
"main": 126218,
"polyfills": 37226
"polyfills": 37778
}
}
},
Expand All @@ -16,7 +16,7 @@
"main": "Likely there is a missing PURE annotation https://github.com/angular/angular/pull/43344",
"main": "Tracking issue: https://github.com/angular/angular/issues/43568",
"main": 20378,
"polyfills": 37250
"polyfills": 37802
}
}
},
Expand All @@ -25,7 +25,7 @@
"uncompressed": {
"runtime": 1105,
"main": 131882,
"polyfills": 37248
"polyfills": 37800
}
}
},
Expand All @@ -34,7 +34,7 @@
"uncompressed": {
"runtime": 929,
"main": 124544,
"polyfills": 37933
"polyfills": 38488
}
}
},
Expand All @@ -43,7 +43,7 @@
"uncompressed": {
"runtime": 2835,
"main": 233348,
"polyfills": 37244,
"polyfills": 37796,
"src_app_lazy_lazy_module_ts": 795
}
}
Expand All @@ -53,7 +53,7 @@
"uncompressed": {
"runtime": 1063,
"main": 158556,
"polyfills": 36975
"polyfills": 37758
}
}
},
Expand All @@ -62,7 +62,7 @@
"uncompressed": {
"runtime": 1070,
"main": 158300,
"polyfills": 37242
"polyfills": 37768
}
}
},
Expand All @@ -77,4 +77,4 @@
}
}
}
}
}
46 changes: 45 additions & 1 deletion packages/zone.js/lib/common/promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
const promiseState = (promise as any)[symbolState];
const delegate = promiseState ?
(typeof onFulfilled === 'function') ? onFulfilled : forwardResolution :
(typeof onRejected === 'function') ? onRejected : forwardRejection;
(typeof onRejected === 'function') ? onRejected :
forwardRejection;
zone.scheduleMicroTask(source, () => {
try {
const parentPromiseValue = (promise as any)[symbolValue];
Expand Down Expand Up @@ -283,6 +284,8 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr

const noop = function() {};

const AggregateError = global.AggregateError;

class ZoneAwarePromise<R> implements Promise<R> {
static toString() {
return ZONE_AWARE_PROMISE_TO_STRING;
Expand All @@ -296,6 +299,47 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
return resolvePromise(<ZoneAwarePromise<U>>new this(null as any), REJECTED, error);
}

static any<T>(values: Iterable<PromiseLike<T>>): Promise<T> {
if (!values || typeof values[Symbol.iterator] !== 'function') {
return Promise.reject(new AggregateError([], 'All promises were rejected'));
}
const promises: Promise<PromiseLike<T>>[] = [];
let count = 0;
try {
for (let v of values) {
count++;
promises.push(ZoneAwarePromise.resolve(v));
}
} catch (err) {
return Promise.reject(new AggregateError([], 'All promises were rejected'));
}
if (count === 0) {
return Promise.reject(new AggregateError([], 'All promises were rejected'));
}
let finished = false;
const errors: any[] = [];
return new ZoneAwarePromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
v => {
if (finished) {
return;
}
finished = true;
resolve(v);
},
err => {
errors.push(err);
count--;
if (count === 0) {
finished = true;
reject(new AggregateError(errors, 'All promises were rejected'));
}
});
}
});
};

static race<R>(values: PromiseLike<any>[]): Promise<R> {
let resolve: (v: any) => void;
let reject: (v: any) => void;
Expand Down
115 changes: 115 additions & 0 deletions packages/zone.js/test/common/Promise.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,121 @@ describe(
});
});

describe('Promise.any', () => {
const any = (Promise as any).any;
it('undefined parameters', (done: DoneFn) => {
any().then(
() => {
fail('should not get a resolved promise.');
},
(err: any) => {
expect(err.message).toEqual('All promises were rejected');
expect(err.errors).toEqual([]);
done();
});
});
it('invalid iterable', (done: DoneFn) => {
const invalidIterable: any = {};
invalidIterable[Symbol.iterator] = () => 2;
any(invalidIterable)
.then(
() => {
fail('should not get a resolved promise.');
},
(err: any) => {
expect(err.message).toEqual('All promises were rejected');
expect(err.errors).toEqual([]);
done();
});
});
it('empty parameters', (done: DoneFn) => {
any([]).then(
() => {
fail('should not get a resolved promise.');
},
(err: any) => {
expect(err.message).toEqual('All promises were rejected');
expect(err.errors).toEqual([]);
done();
});
});
it('non promises parameters', (done: DoneFn) => {
any([1, 'test'])
.then(
(v: any) => {
expect(v).toBe(1);
done();
},
(err: any) => {
fail('should not get a rejected promise.');
});
});
it('mixed parameters, non promise first', (done: DoneFn) => {
any([1, Promise.resolve(2)])
.then(
(v: any) => {
expect(v).toBe(1);
done();
},
(err: any) => {
fail('should not get a rejected promise.');
});
});
it('mixed parameters, promise first', (done: DoneFn) => {
any([Promise.resolve(1), 2])
.then(
(v: any) => {
expect(v).toBe(1);
done();
},
(err: any) => {
fail('should not get a rejected promise.');
});
});
it('all ok promises', (done: DoneFn) => {
any([Promise.resolve(1), Promise.resolve(2)])
.then(
(v: any) => {
expect(v).toBe(1);
done();
},
(err: any) => {
fail('should not get a rejected promise.');
});
});
it('all promises, first rejected', (done: DoneFn) => {
any([Promise.reject('error'), Promise.resolve(2)])
.then(
(v: any) => {
expect(v).toBe(2);
done();
},
(err: any) => {
fail('should not get a rejected promise.');
});
});
it('all promises, second rejected', (done: DoneFn) => {
any([Promise.resolve(1), Promise.reject('error')])
.then(
(v: any) => {
expect(v).toBe(1);
done();
},
(err: any) => {
fail('should not get a rejected promise.');
});
});
it('all rejected promises', (done: DoneFn) => {
any([
Promise.reject('error1'), Promise.reject('error2')
]).then((v: any) => {fail('should not get a resolved promise.')}, (err: any) => {
expect(err.message).toEqual('All promises were rejected');
expect(err.errors).toEqual(['error1', 'error2']);
done();
});
});
});

describe('Promise.allSettled', () => {
const yes = function makeFulfilledResult(value: any) {
return {status: 'fulfilled', value: value};
Expand Down
4 changes: 2 additions & 2 deletions packages/zone.js/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"target": "es2015",
"outDir": "build",
"inlineSourceMap": true,
"inlineSources": true,
Expand Down Expand Up @@ -38,4 +38,4 @@
"test/node_error_entry_point.ts",
"test/node_tests.ts"
]
}
}

0 comments on commit f223349

Please sign in to comment.