Working in Angular 13 so rxjs 6.6 and I'm confused by the following code that doesn't work with forkJoin. The forkJoin never emits. But if I change the of() to timer(0) (or really timer of any value) it does emit.
Yes I can change to combineLatest but I'm trying to understand why the code doesn't work not just how to work around the issue.
function testOne$(): Observable<string> {
const s = new Subject<string>();
of(1000).subscribe(
(val) => {
s.next("Yo!");
s.complete();
}
)
return s.asObservable();
}
function testTwo$(): Observable<string> {
const s = new Subject<string>();
of(2000).subscribe(
(val) => {
s.next("Yea ?");
s.complete();
}
)
return s.asObservable();
}
forkJoin(
{ one: testOne$(),
two: testTwo$() }
).subscribe(
(res) => {
console.warn(res)
}
)
CodePudding user response:
of(1000)
is an observable that emits 1000 and then completes synchronously
So:
function testOne$(): Observable<string> {
const s = new Subject<string>();
of(1000).subscribe(
(val) => {
s.next("Yo!");
s.complete(); // This line is run before...
}
)
// before we get to this line.
// Right now, s is a completed and closed observable.
return s.asObservable();
}
However timer(0)
uses an asynchronous
scheduler, even if you give it zero milliseconds. It still gets scheduled via JS's event loop. Therefore, all the synchronous code will run first and the forkJoin
will be handed an observable that hasn't closed yet.
In RxJS Subject
s act as the mediator for multicasting and also as a means of transitioning imperative and declarative RxJS code. The boundaries between two two styles of coding is fraught with potential foot-guns. You've run into one of them.
If I saw this type of code anywhere in a code review, I'd reject pretty quickly if it wasn't accompanied with a good reason for why the transition couldn't be done in a more principled manner. I can't really advise you here since it's clear you've simplified from a more complex problem.
Otherwise, you'd just write:
function testOne$(): Observable<string> {
return of("Yo!");
}