Let's look at this piece of code:
let isNull = true;
const a$ = new Subject<void>();
const b$ = a$.pipe(
switchMap(() => {
if (!isNull)
return of(true).pipe(
tap(() => console.log('should be shared')),
delay(100) // (3)
);
else return of(null);
}),
shareReplay(1)
);
b$.subscribe((b) => console.log('1', b));
a$.next(); // (1)
b$.subscribe((b) => console.log('2', b));
isNull = false;
a$.next(); // (2)
b$.subscribe((b) => console.log('3', b));
The output is the following:
1 null
2 null
should be shared
3 null // *
1 true
2 true
3 true
Expected output:
1 null
2 null
should be shared
1 true
2 true
3 true
The line marked with * is not desired and I am unsure where it comes from. I assume that the last Observable switchMap returns is not the one updated by (2) but the old one from (1). If I remove the delay at (3), the line marked with * does not appear as expected. This sounds like a timing/ scheduling issue but I do not know a way
- to explain this exactly and to
- fix this elegantly and without unnecessary
nexting
EDIT: Made the example more concise.
EDIT 2:
The reason why I am getting null is that during the time of the third subscription, shareReplay does not have the fresh value (true) yet. Instead, it returns the stale value, which is null. A partially correct workaround is to do this:
let isNull = true;
const a$ = new Subject<void>();
const b$ = a$.pipe(
switchMap(() => {
if (!isNull)
return of(true).pipe(
tap(() => console.log('should be shared')),
delay(100),
shareReplay(1)
);
else return of(null).pipe(shareReplay(1));
}),
share()
);
b$.subscribe((b) => console.log('1', b));
a$.next();
b$.subscribe((b) => console.log('2', b));
isNull = false;
a$.next();
b$.subscribe((b) => console.log('3', b));
// Output:
/*
1 null
should be shared
1 true
2 true
3 true
*/
But as you see, the output "2 null" is missing. So, I am still not sure how to solve this issue elegantly.
The reason why
nullis logged instead oftrueis that the emission oftruereaches theshareReplay(1)operator after it has already returned the previously buffered value (null) to the second subscriber.This happens because the
delayoperator schedules the emission oftruewithin a macrotask, which occurs after the synchronous subscription process is completed (whereinshareReplay(1)returns its buffered value to the subscriber). Therefore, the update ofshareReplay(1)'s buffer does not happen in time or, to put it another way, the subscriber does not wait for the update to complete.This should solve the problem (adapted from here):