RxJS Mastery – #38 switchMap

The RxJS switchMap operator is one of those map operators that flattens inner Observables. Beside that it applies the switch logic. That means the operator switches to the most recent inner Observable and merges those values into the output.

RxJS switchMap always switches to the newest inner Observable

Let’s see the example below where the source Observable emits the letters a,b,c,d. The switchMap operator maps an inner Observable that itself emits two values. The values from the inner Observable are a little bit delayed. They are not merged into the output immediately but (theoretically) only after 100ms.

of('a','b','c','d').pipe(
    switchMap(x => of(`1.${x}`, `2.${x}`).pipe(delay(100)))
).subscribe(console.log);

// 1.d
// 2.d

We see that the final output is 1.d, 2.d. This is because at the time a, b, or c are mapped in the inner Observable, always a new value arrives and switchMap switches to a new inner Observable. 1.c, 2.c would have been ready to be merged into the output. But then the value d from the source arrives. Finally 1.d and 2.d are not “interrupted” anymore by a further switch and are therefore merged into the output stream. Importantly, above example doesn’t merge a, b, and c into the output stream because the of operator emits immediately.

Switch only cancels values not emitted yet

We bring a little bit more time into the next example to show the behaviour of switchMap. The source values are delivered by interval every 1s, limited to 3 values by take. The switchMap of course handles again an inner Observable. In this case the inner Observable emits every 700ms and is limited to 2 values by take. Because the first value of the inner Observable is delivered quicker than the next value of the source Observable the switch doesn’t happen before the first value is merged into the output stream. Therefore the first values are part of the output. These are the values having the 0 after the dot.

interval(1000).pipe(
    take(3),
    switchMap(x => interval(700).pipe(
            take(2),
            map(y => `${x}.${y}`),
        )
    )
).subscribe(console.log);
// 0.0
// 1.0
// 2.0
// 2.1

The second value from the inner Observable is interrupted by the next value from the source. Therefore we don’t see 0.1 and 1.1 in the final output. Only 2.1 is merged into the output stream because no switch happens anymore.

Exercise for the RxJS switchMap operator 💪

Implement a search box that hits the following API. Trigger the search on every character change but only if the characters count is above 2. Make sure running requests are cancelled when a new search is triggered. The code below can give you a head start. It offers a search API for fields on JS window. So, you can try to query it with values like ‘load’ or ‘state’.

const windowMethodsAndFields: string[] = [];
for (let field in window) {
    windowMethodsAndFields.push(field.toString());
}

const search = (characters: string) => new Observable(observer => {
    const SEARCH_REQUEST_DELAY = 1000;
    const result = windowMethodsAndFields.filter(field => field.includes(characters));

    setTimeout(() => {
        observer.next(result);
        observer.complete();
    }, SEARCH_REQUEST_DELAY);
});

As always the code examples can be found on GitHub.