|
5 | 5 | * Use of this source code is governed by an MIT-style license that can be
|
6 | 6 | * found in the LICENSE file at https://angular.io/license
|
7 | 7 | */
|
| 8 | +interface Promise<T> { |
| 9 | + finally<U>(onFinally?: () => U | PromiseLike<U>): Promise<T>; |
| 10 | +} |
| 11 | + |
8 | 12 | Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
|
9 | 13 | const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
10 | 14 | const ObjectDefineProperty = Object.defineProperty;
|
@@ -88,6 +92,9 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
|
88 | 92 |
|
89 | 93 | const symbolState: string = __symbol__('state');
|
90 | 94 | const symbolValue: string = __symbol__('value');
|
| 95 | + const symbolFinally: string = __symbol__('finally'); |
| 96 | + const symbolParentPromiseValue: string = __symbol__('parentPromiseValue'); |
| 97 | + const symbolParentPromiseState: string = __symbol__('parentPromiseState'); |
91 | 98 | const source: string = 'Promise.then';
|
92 | 99 | const UNRESOLVED: null = null;
|
93 | 100 | const RESOLVED = true;
|
@@ -163,6 +170,16 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
|
163 | 170 | const queue = (promise as any)[symbolValue];
|
164 | 171 | (promise as any)[symbolValue] = value;
|
165 | 172 |
|
| 173 | + if ((promise as any)[symbolFinally] === symbolFinally) { |
| 174 | + // the promise is generated by Promise.prototype.finally |
| 175 | + if (state === RESOLVED) { |
| 176 | + // the state is resolved, should ignore the value |
| 177 | + // and use parent promise value |
| 178 | + (promise as any)[symbolState] = (promise as any)[symbolParentPromiseState]; |
| 179 | + (promise as any)[symbolValue] = (promise as any)[symbolParentPromiseValue]; |
| 180 | + } |
| 181 | + } |
| 182 | + |
166 | 183 | // record task information in value when error occurs, so we can
|
167 | 184 | // do some additional work such as render longStackTrace
|
168 | 185 | if (state === REJECTED && value instanceof Error) {
|
@@ -231,14 +248,24 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
|
231 | 248 | promise: ZoneAwarePromise<any>, zone: AmbientZone, chainPromise: ZoneAwarePromise<any>,
|
232 | 249 | onFulfilled?: (value: R) => U1, onRejected?: (error: any) => U2): void {
|
233 | 250 | clearRejectedNoCatch(promise);
|
234 |
| - const delegate = (promise as any)[symbolState] ? |
| 251 | + const promiseState = (promise as any)[symbolState]; |
| 252 | + const delegate = promiseState ? |
235 | 253 | (typeof onFulfilled === 'function') ? onFulfilled : forwardResolution :
|
236 | 254 | (typeof onRejected === 'function') ? onRejected : forwardRejection;
|
237 | 255 | zone.scheduleMicroTask(source, () => {
|
238 | 256 | try {
|
239 |
| - resolvePromise( |
240 |
| - chainPromise, true, zone.run(delegate, undefined, [(promise as any)[symbolValue]])); |
| 257 | + const parentPromiseValue = (promise as any)[symbolValue]; |
| 258 | + const isFinallyPromise = chainPromise && symbolFinally === (chainPromise as any)[symbolFinally]; |
| 259 | + if (isFinallyPromise) { |
| 260 | + // if the promise is generated from finally call, keep parent promise's state and value |
| 261 | + (chainPromise as any)[symbolParentPromiseValue] = parentPromiseValue; |
| 262 | + (chainPromise as any)[symbolParentPromiseState] = promiseState; |
| 263 | + } |
| 264 | + // should not pass value to finally callback |
| 265 | + const value = zone.run(delegate, undefined, isFinallyPromise && delegate !== forwardRejection && delegate !== forwardResolution ? [] : [parentPromiseValue]); |
| 266 | + resolvePromise(chainPromise, true, value); |
241 | 267 | } catch (error) {
|
| 268 | + // if error occurs, should always return this error |
242 | 269 | resolvePromise(chainPromise, false, error);
|
243 | 270 | }
|
244 | 271 | });
|
@@ -345,6 +372,19 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
|
345 | 372 | null): Promise<R|TResult> {
|
346 | 373 | return this.then(null, onRejected);
|
347 | 374 | }
|
| 375 | + |
| 376 | + finally<U>(onFinally?: () => U | PromiseLike<U>): Promise<R> { |
| 377 | + const chainPromise: Promise<R|never> = |
| 378 | + new (this.constructor as typeof ZoneAwarePromise)(null); |
| 379 | + (chainPromise as any)[symbolFinally] = symbolFinally; |
| 380 | + const zone = Zone.current; |
| 381 | + if ((this as any)[symbolState] == UNRESOLVED) { |
| 382 | + (<any[]>(this as any)[symbolValue]).push(zone, chainPromise, onFinally, onFinally); |
| 383 | + } else { |
| 384 | + scheduleResolveOrReject(this, zone, chainPromise, onFinally, onFinally); |
| 385 | + } |
| 386 | + return chainPromise; |
| 387 | + } |
348 | 388 | }
|
349 | 389 | // Protect against aggressive optimizers dropping seemingly unused properties.
|
350 | 390 | // E.g. Closure Compiler in advanced mode.
|
|
0 commit comments