RxJS Mastery – #57 switchAll

The RxJS switchAll operator flattens higher-order Observable. It considers the values from the most recent inner Observable. This means it switches to a newer inner Observable if possible.

RxJS switchAll flattens and switches

The switch logic in RxJS usually switches to the most recent values. This is the same here for switchAll. Only the most recent inner Observable is subscribed to. In the below code, there is a source$ Observable with 3 inner Observables. As you can see the inner Observables have different subscription points, A and B are two timeframes apart, same applies to B and C.

const innerA = cold('  aaaaa|', { a: 'a' });
const innerB = cold('    bbbb|', { b: 'b' });
const innerC = cold('      ccc|', { c: 'c' });

const source$ = hot('--A-B-C--|', { A: innerA, B: innerB, C: innerC });
const result$ = source$.pipe(switchAll());

expectObservable(result$).toBe(
    '--aabbccc|',
    { a: 'a', b: 'b', c: 'c' }
);

As soon as switchAll detects a new inner Observable, it subscribes to it and only takes values from that Observable.

For switchAll the subscription points are decisive

We slightly modify the above example and add a timeframe to the beginning of the innerC Observable. During that time frame, no value is emitted. Nevertheless the emission from the innerB stops after two b‘s. This is because after 2 emissions from innerB, the subscription to innerC happens. So, it is important to note that not the emission of values, but the subscription decides about the most recent inner Observable that is taken into account.

const innerA = cold('  aaaaa|', { a: 'a' });
const innerB = cold('    bbbb|', { b: 'b' });
const innerC = cold('      -ccc|', { c: 'c' });

const source$ = hot('--A-B-C--|', { A: innerA, B: innerB, C: innerC });
const result$ = source$.pipe(switchAll());

expectObservable(result$).toBe(
    '--aabb-ccc|',
    { a: 'a', b: 'b', c: 'c' }
);

A switch therefore does not necessarily result in any value emission. No b‘s are in the final result$:

const innerA = cold('  aaaaa|', { a: 'a' });
const innerB = cold('    ----|', { b: 'b' });
const innerC = cold('      -ccc|', { c: 'c' });

const source$ = hot('--A-B-C--|', { A: innerA, B: innerB, C: innerC });
const result$ = source$.pipe(switchAll());

expectObservable(result$).toBe(
    '--aa---ccc|',
    { a: 'a', c: 'c' }
);

Exercise for the RxJS switchAll operator 💪

We have a news feed on our web application. And there is an impatient user that triggers the refresh of our feed 5 times in short succession (see t). Make sure to not overload the server but still always try to fetch the latest news by considering the latest refresh. Should a fetch news request still be running when a new one is needed, the older request can be canceled.

const fetchNews = (): Observable<string> => cold('--r|', { r: 'Latest news' });

const source$ = hot('--t-t--tt---t|', {t: fetchNews()});
const feed$ = source$ // handle the feed

expectObservable(feed$).toBe(
    '------n--', // complete the marbles
    { n: 'Latest news' }
);

This post is part of the RxJS mastery series. As always the code examples can be found on GitHub.