-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Bug Report
Current Behavior
When you have an observable with circular references, something like:
const deferredCount = defer(() => source).pipe(
startWith(0)
);
const source: Observable<string> = from([1, 2, 3]).pipe(
withLatestFrom(deferredCount),
map(([a, b]) => String(Number(a) + Number(b))),
share()
);(Sorry for the contrived example, this could be easily done with a scan, but shows the essential shape of a circular reference in observables)
Share will throw an error. I've seen two different errors depending on the environment I'm running this test, but both of them look similar. When testing with rxjs-marbles:
TypeError: Cannot read property 'complete' of null
at Object.complete (node_modules/rxjs/src/internal/operators/share.ts:138:10)
at Object.complete (node_modules/rxjs/src/internal/Subscriber.ts:188:14)
at SafeSubscriber.Object.<anonymous>.Subscriber._complete (node_modules/rxjs/src/internal/Subscriber.ts:126:24)
at SafeSubscriber.Object.<anonymous>.Subscriber.complete (node_modules/rxjs/src/internal/Subscriber.ts:100:12)
at OperatorSubscriber.Object.<anonymous>.Subscriber._complete (node_modules/rxjs/src/internal/Subscriber.ts:126:24)
at OperatorSubscriber.Object.<anonymous>.Subscriber.complete (node_modules/rxjs/src/internal/Subscriber.ts:100:12)
at OperatorSubscriber.Object.<anonymous>.Subscriber._complete (node_modules/rxjs/src/internal/Subscriber.ts:126:24)
at OperatorSubscriber.Object.<anonymous>.Subscriber.complete (node_modules/rxjs/src/internal/Subscriber.ts:100:12)
at Object.observeNotification (node_modules/rxjs/src/internal/Notification.ts:238:102)
at VirtualAction.subscriber.add.scheduler.schedule.message (node_modules/rxjs/src/internal/testing/ColdObservable.ts:43:13)
at VirtualAction.Object.<anonymous>.AsyncAction._execute (node_modules/rxjs/src/internal/scheduler/AsyncAction.ts:116:12)
at VirtualAction.Object.<anonymous>.VirtualAction._execute (node_modules/rxjs/src/internal/scheduler/VirtualTimeScheduler.ts:111:28)
at VirtualAction.Object.<anonymous>.AsyncAction.execute (node_modules/rxjs/src/internal/scheduler/AsyncAction.ts:91:24)
at TestScheduler.Object.<anonymous>.VirtualTimeScheduler.flush (node_modules/rxjs/src/internal/scheduler/VirtualTimeScheduler.ts:52:26)
at TestScheduler.Object.<anonymous>.TestScheduler.flush (node_modules/rxjs/src/internal/testing/TestScheduler.ts:212:16)
at TestScheduler.Object.<anonymous>.TestScheduler.run (node_modules/rxjs/src/internal/testing/TestScheduler.ts:678:12)
at Object.wrapper (node_modules/rxjs-marbles/marbles.js:49:36)
In a playground:
Uncaught TypeError: Cannot read property 'next' of null
at Object.next (share.js:31)
at Object.eval [as next] (Subscriber.js:133)
at SafeSubscriber.Subscriber._next (Subscriber.js:77)
at SafeSubscriber.Subscriber.next (Subscriber.js:50)
at eval (map.js:13)
at OperatorSubscriber._this._next (OperatorSubscriber.js:22)
at OperatorSubscriber.Subscriber.next (Subscriber.js:50)
at eval (withLatestFrom.js:42)
at OperatorSubscriber._this._next (OperatorSubscriber.js:22)
at OperatorSubscriber.Subscriber.next (Subscriber.js:50)
at Observable.eval [as _subscribe] (from.js:59)
at Observable._trySubscribe (Observable.js:52)
at Observable.subscribe (Observable.js:38)
at eval (withLatestFrom.js:39)
at OperatorSubscriber.eval (lift.js:17)
at Observable.subscribe (Observable.js:38)
at eval (map.js:12)
at SafeSubscriber.eval (lift.js:17)
at Observable.subscribe (Observable.js:38)
at eval (share.js:29)
at OperatorSubscriber.eval (lift.js:17)
at Observable.subscribe (Observable.js:38)
at Observable.eval [as _subscribe] (defer.js:11)
at Observable._trySubscribe (Observable.js:52)
at Observable.subscribe (Observable.js:38)
at mergeInternals (mergeInternals.js:55)
at eval (mergeMap.js:26)
at OperatorSubscriber.eval (lift.js:17)
at Observable.subscribe (Observable.js:38)
at doInnerSub (mergeInternals.js:26)
at outerNext (mergeInternals.js:20)
at OperatorSubscriber._this._next (OperatorSubscriber.js:22)
at OperatorSubscriber.Subscriber.next (Subscriber.js:50)
at Observable.eval [as _subscribe] (from.js:59)
at Observable._trySubscribe (Observable.js:52)
at Observable.subscribe (Observable.js:38)
at mergeInternals (mergeInternals.js:55)
at eval (mergeMap.js:26)
at OperatorSubscriber.eval (lift.js:17)
at Observable.subscribe (Observable.js:38)
at eval (startWith.js:17)
at OperatorSubscriber.eval (lift.js:17)
at Observable.subscribe (Observable.js:38)
at _loop_1 (withLatestFrom.js:28)
at eval (withLatestFrom.js:37)
at OperatorSubscriber.eval (lift.js:17)
at Observable.subscribe (Observable.js:38)
at eval (map.js:12)
at SafeSubscriber.eval (lift.js:17)
at Observable.subscribe (Observable.js:38)
Expected behavior
In v6.x it worked as expected, without throwing any error.
Reproduction
https://codesandbox.io/s/stupefied-cdn-x92sk?file=/src/index.ts
Environment
- Runtime: any
- RxJS version: 7.0.0-beta.13
Additional context/Screenshots
It looks like subject at this point is null, so it breaks when calling .next, .error or .complete... but by reading the code I can't seem to understand how it's possible that it has a null value, as it seems like it's only set in here?
I tried adding a delay(x) on the deferredCount to check if it's because it's something synchronous, but that doesn't seem to help either.
Also note these kind of circular references in v6 always keep a subscriber open, which I guess it's discouraged. If I break this circular reference with a subject:
const countSubject = new Subject<string>();
const source: Observable<string> = from([1, 2, 3]).pipe(
withLatestFrom(countSubject.pipe(startWith(0))),
map(([a, b]) => String(Number(a) + Number(b))),
tap(countSubject),
share()
);It doesn't throw any exception.