Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
feat(promise): support Promise.prototype.finally (#1005)
Browse files Browse the repository at this point in the history
  • Loading branch information
JiaLiPassion authored and mhevery committed Feb 10, 2018
1 parent 5c139e5 commit 6a1a830
Show file tree
Hide file tree
Showing 7 changed files with 435 additions and 4 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ script:
- node_modules/.bin/gulp filesize
- scripts/closure/closure_compiler.sh
- node_modules/.bin/gulp promisetest
- npm run promisefinallytest
- npm run test:phantomjs-single
- node_modules/.bin/karma start karma-dist-sauce-jasmine.conf.js --single-run
- node_modules/.bin/karma start karma-build-sauce-mocha.conf.js --single-run
Expand Down
2 changes: 2 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ gulp.task('promisetest', ['build/zone-node.js'], (cb) => {
promisesAplusTests(adapter, { reporter: "dot" }, function (err) {
if (err) {
cb(err);
} else {
cb();
}
});
});
Expand Down
46 changes: 43 additions & 3 deletions lib/common/promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
interface Promise<T> {
finally<U>(onFinally?: () => U | PromiseLike<U>): Promise<T>;
}

Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
const ObjectDefineProperty = Object.defineProperty;
Expand Down Expand Up @@ -88,6 +92,9 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr

const symbolState: string = __symbol__('state');
const symbolValue: string = __symbol__('value');
const symbolFinally: string = __symbol__('finally');
const symbolParentPromiseValue: string = __symbol__('parentPromiseValue');
const symbolParentPromiseState: string = __symbol__('parentPromiseState');
const source: string = 'Promise.then';
const UNRESOLVED: null = null;
const RESOLVED = true;
Expand Down Expand Up @@ -163,6 +170,16 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
const queue = (promise as any)[symbolValue];
(promise as any)[symbolValue] = value;

if ((promise as any)[symbolFinally] === symbolFinally) {
// the promise is generated by Promise.prototype.finally
if (state === RESOLVED) {
// the state is resolved, should ignore the value
// and use parent promise value
(promise as any)[symbolState] = (promise as any)[symbolParentPromiseState];
(promise as any)[symbolValue] = (promise as any)[symbolParentPromiseValue];
}
}

// record task information in value when error occurs, so we can
// do some additional work such as render longStackTrace
if (state === REJECTED && value instanceof Error) {
Expand Down Expand Up @@ -231,14 +248,24 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
promise: ZoneAwarePromise<any>, zone: AmbientZone, chainPromise: ZoneAwarePromise<any>,
onFulfilled?: (value: R) => U1, onRejected?: (error: any) => U2): void {
clearRejectedNoCatch(promise);
const delegate = (promise as any)[symbolState] ?
const promiseState = (promise as any)[symbolState];
const delegate = promiseState ?
(typeof onFulfilled === 'function') ? onFulfilled : forwardResolution :
(typeof onRejected === 'function') ? onRejected : forwardRejection;
zone.scheduleMicroTask(source, () => {
try {
resolvePromise(
chainPromise, true, zone.run(delegate, undefined, [(promise as any)[symbolValue]]));
const parentPromiseValue = (promise as any)[symbolValue];
const isFinallyPromise = chainPromise && symbolFinally === (chainPromise as any)[symbolFinally];
if (isFinallyPromise) {
// if the promise is generated from finally call, keep parent promise's state and value
(chainPromise as any)[symbolParentPromiseValue] = parentPromiseValue;
(chainPromise as any)[symbolParentPromiseState] = promiseState;
}
// should not pass value to finally callback
const value = zone.run(delegate, undefined, isFinallyPromise && delegate !== forwardRejection && delegate !== forwardResolution ? [] : [parentPromiseValue]);
resolvePromise(chainPromise, true, value);
} catch (error) {
// if error occurs, should always return this error
resolvePromise(chainPromise, false, error);
}
});
Expand Down Expand Up @@ -345,6 +372,19 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
null): Promise<R|TResult> {
return this.then(null, onRejected);
}

finally<U>(onFinally?: () => U | PromiseLike<U>): Promise<R> {
const chainPromise: Promise<R|never> =
new (this.constructor as typeof ZoneAwarePromise)(null);
(chainPromise as any)[symbolFinally] = symbolFinally;
const zone = Zone.current;
if ((this as any)[symbolState] == UNRESOLVED) {
(<any[]>(this as any)[symbolValue]).push(zone, chainPromise, onFinally, onFinally);
} else {
scheduleResolveOrReject(this, zone, chainPromise, onFinally, onFinally);
}
return chainPromise;
}
}
// Protect against aggressive optimizers dropping seemingly unused properties.
// E.g. Closure Compiler in advanced mode.
Expand Down
11 changes: 10 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"lint": "gulp lint",
"prepublish": "tsc && gulp build",
"promisetest": "gulp promisetest",
"promisefinallytest": "mocha promise.finally.spec.js",
"webdriver-start": "webdriver-manager update && webdriver-manager start",
"webdriver-http": "node simple-server.js",
"webdriver-test": "node test/webdriver/test.js",
Expand Down Expand Up @@ -59,6 +60,7 @@
"@types/jasmine": "2.2.33",
"@types/node": "^6.0.96",
"@types/systemjs": "^0.19.30",
"assert": "^1.4.1",
"clang-format": "1.0.46",
"concurrently": "^2.2.0",
"conventional-changelog": "^1.1.7",
Expand Down
Loading

0 comments on commit 6a1a830

Please sign in to comment.