Skip to content

Commit b0bbce2

Browse files
committed
Merge remote-tracking branch 'upstream/master' into observable
2 parents 0c79578 + c666574 commit b0bbce2

File tree

6 files changed

+57
-159
lines changed

6 files changed

+57
-159
lines changed

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

Lines changed: 21 additions & 44 deletions
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} */
@@ -33,19 +33,6 @@ describe('every', () => {
3333
});
3434
});
3535

36-
it('should accept thisArg with scalar observables', () => {
37-
const thisArg = {};
38-
39-
of(1)
40-
.pipe(
41-
every(function (this: any, value: number, index: number) {
42-
expect(this).to.deep.equal(thisArg);
43-
return true;
44-
}, thisArg)
45-
)
46-
.subscribe();
47-
});
48-
4936
it('should increment index on each call to the predicate', () => {
5037
const indices: number[] = [];
5138
of(1, 2, 3, 4)
@@ -60,36 +47,6 @@ describe('every', () => {
6047
expect(indices).to.deep.equal([0, 1, 2, 3]);
6148
});
6249

63-
it('should accept thisArg with array observable', () => {
64-
const thisArg = {};
65-
66-
of(1, 2, 3, 4)
67-
.pipe(
68-
every(function (this: any, value: number, index: number) {
69-
expect(this).to.deep.equal(thisArg);
70-
return true;
71-
}, thisArg)
72-
)
73-
.subscribe();
74-
});
75-
76-
it('should accept thisArg with ordinary observable', () => {
77-
const thisArg = {};
78-
79-
const source = new Observable((observer: Observer<number>) => {
80-
observer.next(1);
81-
observer.complete();
82-
});
83-
source
84-
.pipe(
85-
every(function (this: any, value: number, index: number) {
86-
expect(this).to.deep.equal(thisArg);
87-
return true;
88-
}, thisArg)
89-
)
90-
.subscribe();
91-
});
92-
9350
it('should emit true if source is empty', () => {
9451
testScheduler.run(({ hot, expectObservable, expectSubscriptions }) => {
9552
const e1 = hot(' -----| ');
@@ -344,4 +301,24 @@ describe('every', () => {
344301
expectSubscriptions(e1.subscriptions).toBe(e1subs);
345302
});
346303
});
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+
});
347324
});

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

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,6 @@ describe('expand', () => {
3030
});
3131
});
3232

33-
it('should work with scheduler', () => {
34-
testScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => {
35-
const e1 = hot(' --x----| ', { x: 1 });
36-
const e1subs = ' ^------! ';
37-
const e2 = cold(' --c| ', { c: 2 });
38-
// --c|
39-
// --c|
40-
const expected = '--a-b-c-d|';
41-
const values = { a: 1, b: 2, c: 4, d: 8 };
42-
43-
const result = e1.pipe(expand((x) => (x === 8 ? EMPTY : e2.pipe(map((c) => c * x))), Infinity, testScheduler));
44-
45-
expectObservable(result).toBe(expected, values);
46-
expectSubscriptions(e1.subscriptions).toBe(e1subs);
47-
});
48-
});
49-
5033
it('should map and recursively flatten', () => {
5134
testScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => {
5235
const values = {
@@ -470,35 +453,13 @@ describe('expand', () => {
470453
return cold(e2shape, { z: x + x });
471454
};
472455

473-
const result = e1.pipe(expand(project, undefined, undefined));
456+
const result = e1.pipe(expand(project, undefined));
474457

475458
expectObservable(result).toBe(expected, values);
476459
expectSubscriptions(e1.subscriptions).toBe(e1subs);
477460
});
478461
});
479462

480-
it('should work with the AsapScheduler', (done) => {
481-
const expected = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
482-
of(0)
483-
.pipe(
484-
expand((x) => of(x + 1), Infinity, asapScheduler),
485-
take(10),
486-
toArray()
487-
)
488-
.subscribe({ next: (actual) => expect(actual).to.deep.equal(expected), error: done, complete: done });
489-
});
490-
491-
it('should work with the AsyncScheduler', (done) => {
492-
const expected = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
493-
of(0)
494-
.pipe(
495-
expand((x) => of(x + 1), Infinity, asyncScheduler),
496-
take(10),
497-
toArray()
498-
)
499-
.subscribe({ next: (actual) => expect(actual).to.deep.equal(expected), error: done, complete: done });
500-
});
501-
502463
it('should stop listening to a synchronous observable when unsubscribed', () => {
503464
const sideEffects: number[] = [];
504465
const synchronousObservable = new Observable<number>((subscriber) => {

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

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,7 @@ import { Observable, operate } from '@rxjs/observable';
22
import type { Falsy, OperatorFunction } from '../types.js';
33

44
export function every<T>(predicate: BooleanConstructor): OperatorFunction<T, Exclude<T, Falsy> extends never ? false : boolean>;
5-
/** @deprecated Use a closure instead of a `thisArg`. Signatures accepting a `thisArg` will be removed in v8. */
6-
export function every<T>(
7-
predicate: BooleanConstructor,
8-
thisArg: any
9-
): OperatorFunction<T, Exclude<T, Falsy> extends never ? false : boolean>;
10-
/** @deprecated Use a closure instead of a `thisArg`. Signatures accepting a `thisArg` will be removed in v8. */
11-
export function every<T, A>(
12-
predicate: (this: A, value: T, index: number, source: Observable<T>) => boolean,
13-
thisArg: A
14-
): OperatorFunction<T, boolean>;
15-
export function every<T>(predicate: (value: T, index: number, source: Observable<T>) => boolean): OperatorFunction<T, boolean>;
5+
export function every<T>(predicate: (value: T, index: number) => boolean): OperatorFunction<T, boolean>;
166

177
/**
188
* Returns an Observable that emits whether or not every item of the source satisfies the condition specified.
@@ -39,27 +29,29 @@ export function every<T>(predicate: (value: T, index: number, source: Observable
3929
* @return A function that returns an Observable of booleans that determines if
4030
* all items of the source Observable meet the condition specified.
4131
*/
42-
export function every<T>(
43-
predicate: (value: T, index: number, source: Observable<T>) => boolean,
44-
thisArg?: any
45-
): OperatorFunction<T, boolean> {
32+
export function every<T>(predicate: (value: T, index: number) => boolean): OperatorFunction<T, boolean> {
4633
return (source) =>
4734
new Observable((destination) => {
4835
let index = 0;
49-
source.subscribe(
50-
operate({
51-
destination,
52-
next: (value) => {
53-
if (!predicate.call(thisArg, value, index++, source)) {
54-
destination.next(false);
55-
destination.complete();
56-
}
57-
},
58-
complete: () => {
59-
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);
6046
destination.complete();
61-
},
62-
})
63-
);
47+
}
48+
},
49+
complete: () => {
50+
destination.next(true);
51+
destination.complete();
52+
},
53+
});
54+
55+
source.subscribe(subscriber);
6456
});
6557
}

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

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
1-
import type { OperatorFunction, ObservableInput, ObservedValueOf, SchedulerLike } from '../types.js';
21
import { Observable } from '@rxjs/observable';
2+
import type { ObservableInput, ObservedValueOf, OperatorFunction } from '../types.js';
33
import { mergeInternals } from './mergeInternals.js';
44

55
export function expand<T, O extends ObservableInput<unknown>>(
66
project: (value: T, index: number) => O,
7-
concurrent?: number,
8-
scheduler?: SchedulerLike
9-
): OperatorFunction<T, ObservedValueOf<O>>;
10-
/**
11-
* @deprecated The `scheduler` parameter will be removed in v8. If you need to schedule the inner subscription,
12-
* use `subscribeOn` within the projection function: `expand((value) => fn(value).pipe(subscribeOn(scheduler)))`.
13-
* Details: Details: https://rxjs.dev/deprecations/scheduler-argument
14-
*/
15-
export function expand<T, O extends ObservableInput<unknown>>(
16-
project: (value: T, index: number) => O,
17-
concurrent: number | undefined,
18-
scheduler: SchedulerLike
7+
concurrent?: number
198
): OperatorFunction<T, ObservedValueOf<O>>;
209

2110
/**
@@ -61,17 +50,14 @@ export function expand<T, O extends ObservableInput<unknown>>(
6150
* or the output Observable, returns an Observable.
6251
* @param concurrent Maximum number of input Observables being subscribed to
6352
* concurrently.
64-
* @param scheduler The {@link SchedulerLike} to use for subscribing to
65-
* each projected inner Observable.
6653
* @return A function that returns an Observable that emits the source values
6754
* and also result of applying the projection function to each value emitted on
6855
* the output Observable and merging the results of the Observables obtained
6956
* from this transformation.
7057
*/
7158
export function expand<T, O extends ObservableInput<unknown>>(
7259
project: (value: T, index: number) => O,
73-
concurrent = Infinity,
74-
scheduler?: SchedulerLike
60+
concurrent = Infinity
7561
): OperatorFunction<T, ObservedValueOf<O>> {
7662
concurrent = (concurrent || 0) < 1 ? Infinity : concurrent;
7763
return (source) =>
@@ -89,8 +75,7 @@ export function expand<T, O extends ObservableInput<unknown>>(
8975
undefined,
9076

9177
// Expand-specific
92-
true, // Use expand path
93-
scheduler // Inner subscription scheduler
78+
true // Use expand path
9479
)
9580
);
9681
}

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

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import type { Observable, Subscriber} from '@rxjs/observable';
1+
import type { Observable, Subscriber } from '@rxjs/observable';
22
import { from, operate } from '@rxjs/observable';
3-
import type { ObservableInput, SchedulerLike } from '../types.js';
4-
import { executeSchedule } from '../util/executeSchedule.js';
3+
import type { ObservableInput } from '../types.js';
54

65
/**
76
* A process embodying the general "merge" strategy. This is used in
@@ -13,18 +12,14 @@ import { executeSchedule } from '../util/executeSchedule.js';
1312
* @param onBeforeNext Additional logic to apply before nexting to our consumer
1413
* @param expand If `true` this will perform an "expand" strategy, which differs only
1514
* in that it recurses, and the inner subscription must be schedule-able.
16-
* @param innerSubScheduler A scheduler to use to schedule inner subscriptions,
17-
* this is to support the expand strategy, mostly, and should be deprecated
1815
*/
1916
export function mergeInternals<T, R>(
2017
source: Observable<T>,
2118
destination: Subscriber<R>,
2219
project: (value: T, index: number) => ObservableInput<R>,
2320
concurrent: number,
2421
onBeforeNext?: (innerValue: R) => void,
25-
expand?: boolean,
26-
innerSubScheduler?: SchedulerLike,
27-
additionalFinalizer?: () => void
22+
expand?: boolean
2823
) {
2924
// Buffered values, in the event of going over our concurrency limit
3025
const buffer: T[] = [];
@@ -107,15 +102,7 @@ export function mergeInternals<T, R>(
107102
// next conditional, if there were any more inner subscriptions
108103
// to start.
109104
while (buffer.length && active < concurrent) {
110-
const bufferedValue = buffer.shift()!;
111-
// Particularly for `expand`, we need to check to see if a scheduler was provided
112-
// for when we want to start our inner subscription. Otherwise, we just start
113-
// are next inner subscription.
114-
if (innerSubScheduler) {
115-
executeSchedule(destination, innerSubScheduler, () => doInnerSub(bufferedValue));
116-
} else {
117-
doInnerSub(bufferedValue);
118-
}
105+
doInnerSub(buffer.shift()!);
119106
}
120107
// Check to see if we can complete, and complete if so.
121108
checkComplete();
@@ -140,10 +127,4 @@ export function mergeInternals<T, R>(
140127
},
141128
})
142129
);
143-
144-
// Additional finalization (for when the destination is torn down).
145-
// Other finalization is added implicitly via subscription above.
146-
return () => {
147-
additionalFinalizer?.();
148-
};
149130
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,19 @@ export function mergeScan<T, R>(
7777
// The accumulated state.
7878
let state = seed;
7979

80-
return mergeInternals(
80+
mergeInternals(
8181
source,
8282
subscriber,
8383
(value, index) => accumulator(state, value, index),
8484
concurrent,
8585
(value) => {
8686
state = value;
8787
},
88-
false,
89-
undefined,
90-
() => (state = null!)
88+
false
9189
);
90+
91+
return () => {
92+
state = null!;
93+
};
9294
});
9395
}

0 commit comments

Comments
 (0)