Skip to content

Commit c666574

Browse files
committed
fix(rxjs): every now properly handles reentrant calls.
Fixes #7425
1 parent 83ffab1 commit c666574

File tree

2 files changed

+40
-15
lines changed

2 files changed

+40
-15
lines changed

packages/rxjs/spec/operators/every-spec.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { expect } from 'chai';
22
import { every, mergeMap } from 'rxjs/operators';
33
import { TestScheduler } from 'rxjs/testing';
44
import type { Observer } from 'rxjs';
5-
import { of, Observable } from 'rxjs';
5+
import { of, Observable, Subject } from 'rxjs';
66
import { observableMatcher } from '../helpers/observableMatcher';
77

88
/** @test {every} */
@@ -301,4 +301,24 @@ describe('every', () => {
301301
expectSubscriptions(e1.subscriptions).toBe(e1subs);
302302
});
303303
});
304+
305+
it('should handle reentrancy properly', () => {
306+
const subject = new Subject<number>();
307+
const results: any[] = [];
308+
let n = 0;
309+
310+
subject.pipe(every(() => false)).subscribe({
311+
next: (result) => {
312+
results.push(result);
313+
if (n < 3) {
314+
subject.next(n++);
315+
}
316+
},
317+
complete: () => results.push('done'),
318+
});
319+
320+
subject.next(n);
321+
322+
expect(results).to.deep.equal([false, 'done']);
323+
});
304324
});

packages/rxjs/src/internal/operators/every.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,25 @@ export function every<T>(predicate: (value: T, index: number) => boolean): Opera
3333
return (source) =>
3434
new Observable((destination) => {
3535
let index = 0;
36-
source.subscribe(
37-
operate({
38-
destination,
39-
next: (value) => {
40-
if (!predicate(value, index++)) {
41-
destination.next(false);
42-
destination.complete();
43-
}
44-
},
45-
complete: () => {
46-
destination.next(true);
36+
37+
const subscriber = operate({
38+
destination,
39+
next: (value: T) => {
40+
if (!predicate(value, index++)) {
41+
// To prevent re-entrancy issues, we unsubscribe from the
42+
// source as soon as possible. Because the `next` right below it
43+
// could cause us to re-enter before we get to `complete()`.
44+
subscriber.unsubscribe();
45+
destination.next(false);
4746
destination.complete();
48-
},
49-
})
50-
);
47+
}
48+
},
49+
complete: () => {
50+
destination.next(true);
51+
destination.complete();
52+
},
53+
});
54+
55+
source.subscribe(subscriber);
5156
});
5257
}

0 commit comments

Comments
 (0)