|
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