TIL - Use Tap in RxJS for side effects

What is the tap operator?

tap marble diagram

I recently learned about the tap operator when trying to perform a side-effect inside of a map operator. While you could perform side-effects inside of a map operator, it's a better practice to do so in a tap operator where it allows you to separate the business logic transformation from the side effects, making your code more modular and easier to maintain.

In other words, using map for both transformations and side effects can lead to "business logic" being intertwined with "infrastructure concerns", which can make it harder to reason about and modify your code. By isolating side effects in a separate tap operator, you can keep your map operators focused on the core transformation logic, making your code more composable, scalable, and maintainable.

How does it work?

When using the tap operator, you can pass a function as an argument. This function will be called whenever the source observable emits a value. The returned observable from tap is identical to the original observable; only the side effect is triggered.

Example Usage

import { from, tap } from 'rxjs';

const numbers = from([1, 2, 3]);
numbers.pipe(tap(x => console.log('Received:', x))).subscribe();

In this example, tap logs each emitted value to the console without affecting the original observable. The subscription will still receive the values as expected.