fix(zone.js): zone patch rxjs should return null _unsubscribe correctly. (#37091)
Close #31684. In some rxjs operator, such as `retryWhen`, rxjs internally will set `Subscription._unsubscribe` method to null, and the current zone.js monkey patch didn't handle this case correctly, even rxjs set _unsubscribe to null, zone.js still return a function by finding the prototype chain. This PR fix this issue and the following test will pass. ``` const errorGenerator = () => { return throwError(new Error('error emit')); }; const genericRetryStrategy = (finalizer: () => void) => (attempts: Observable<any>) => attempts.pipe( mergeMap((error, i) => { const retryAttempt = i + 1; if (retryAttempt > 3) { return throwError(error); } return timer(retryAttempt * 1); }), finalize(() => finalizer())); errorGenerator() .pipe( retryWhen(genericRetryStrategy(() => { expect(log.length).toBe(3); done(); })), catchError(error => of(error))) .subscribe() ``` PR Close #37091
This commit is contained in:
parent
2b6ab57d78
commit
96aa14df01
|
@ -115,7 +115,7 @@ type ZoneSubscriberContext = {
|
||||||
_zoneUnsubscribe: {value: null, writable: true, configurable: true},
|
_zoneUnsubscribe: {value: null, writable: true, configurable: true},
|
||||||
_unsubscribe: {
|
_unsubscribe: {
|
||||||
get: function(this: Subscription) {
|
get: function(this: Subscription) {
|
||||||
if ((this as any)._zoneUnsubscribe) {
|
if ((this as any)._zoneUnsubscribe || (this as any)._zoneUnsubscribeCleared) {
|
||||||
return (this as any)._zoneUnsubscribe;
|
return (this as any)._zoneUnsubscribe;
|
||||||
}
|
}
|
||||||
const proto = Object.getPrototypeOf(this);
|
const proto = Object.getPrototypeOf(this);
|
||||||
|
@ -125,7 +125,13 @@ type ZoneSubscriberContext = {
|
||||||
(this as any)._zone = Zone.current;
|
(this as any)._zone = Zone.current;
|
||||||
if (!unsubscribe) {
|
if (!unsubscribe) {
|
||||||
(this as any)._zoneUnsubscribe = unsubscribe;
|
(this as any)._zoneUnsubscribe = unsubscribe;
|
||||||
|
// In some operator such as `retryWhen`, the _unsubscribe
|
||||||
|
// method will be set to null, so we need to set another flag
|
||||||
|
// to tell that we should return null instead of finding
|
||||||
|
// in the prototype chain.
|
||||||
|
(this as any)._zoneUnsubscribeCleared = true;
|
||||||
} else {
|
} else {
|
||||||
|
(this as any)._zoneUnsubscribeCleared = false;
|
||||||
(this as any)._zoneUnsubscribe = function() {
|
(this as any)._zoneUnsubscribe = function() {
|
||||||
if (this._zone && this._zone !== Zone.current) {
|
if (this._zone && this._zone !== Zone.current) {
|
||||||
return this._zone.run(unsubscribe, this, arguments);
|
return this._zone.run(unsubscribe, this, arguments);
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
import {Observable, of, throwError, timer} from 'rxjs';
|
||||||
|
import {catchError, finalize, mergeMap, retryWhen} from 'rxjs/operators';
|
||||||
|
|
||||||
|
describe('retryWhen', () => {
|
||||||
|
let log: any[];
|
||||||
|
const genericRetryStrategy = (finalizer: () => void) => (attempts: Observable<any>) =>
|
||||||
|
attempts.pipe(
|
||||||
|
mergeMap((error, i) => {
|
||||||
|
const retryAttempt = i + 1;
|
||||||
|
if (retryAttempt > 3) {
|
||||||
|
return throwError(error);
|
||||||
|
}
|
||||||
|
log.push(error);
|
||||||
|
return timer(retryAttempt * 1);
|
||||||
|
}),
|
||||||
|
finalize(() => finalizer()));
|
||||||
|
|
||||||
|
const errorGenerator = () => {
|
||||||
|
return throwError(new Error('error emit'));
|
||||||
|
};
|
||||||
|
beforeEach(() => {
|
||||||
|
log = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should retry max 3 times',
|
||||||
|
(done: DoneFn) => {errorGenerator()
|
||||||
|
.pipe(
|
||||||
|
retryWhen(genericRetryStrategy(() => {
|
||||||
|
expect(log.length).toBe(3);
|
||||||
|
done();
|
||||||
|
})),
|
||||||
|
catchError(error => of(error)))
|
||||||
|
.subscribe()});
|
||||||
|
});
|
|
@ -27,6 +27,7 @@ import './rxjs.merge.spec';
|
||||||
import './rxjs.never.spec';
|
import './rxjs.never.spec';
|
||||||
import './rxjs.of.spec';
|
import './rxjs.of.spec';
|
||||||
import './rxjs.range.spec';
|
import './rxjs.range.spec';
|
||||||
|
import './rxjs.retry.spec';
|
||||||
import './rxjs.throw.spec';
|
import './rxjs.throw.spec';
|
||||||
import './rxjs.timer.spec';
|
import './rxjs.timer.spec';
|
||||||
import './rxjs.zip.spec';
|
import './rxjs.zip.spec';
|
||||||
|
|
Loading…
Reference in New Issue