RxJS

RxJS Operators as Signals: mergeMap(), switchMap(), concatMap(), and exhaustMap()

3 min. read

Asynchronous code is essential for building high-performance, scalable, and responsive applications in modern web development. By enabling concurrent execution, preventing blocking, and improving resource utilization, asynchronous programming plays a critical role in delivering a seamless and engaging user experience.

RxJS provides a comprehensive set of tools and abstractions for working with asynchronous data streams in a reactive and composable manner. RxJS provides a rich set of operators that allow you to manipulate, transform, filter, combine, and manage observable streams. These transformative operators in RxJS are used to transform the data emitted by an observable stream. They allow you to manipulate, filter, combine, or otherwise transform the data in various ways. In a previous post about Common RxJS's transformation operators, we learned the differences between mergeMap(), switchMap(), concatMap(), and exhaustMap(). In this article, we will compare signals and RxJS as well as create a conceptual equivalent of the mentioned operators as signal().

Signals vs RxJS

In the context of Angular, RxJS is often used to handle asynchronous operations and to work with streams of data, such as HTTP requests, user input events, or other asynchronous events.

Signals in Angular can be considered as a higher-level concept that encompasses the use of RxJS observables but is not limited to it. While RxJS provides a powerful way to work with asynchronous data streams using operators like map, filter, merge, and so on, signals in Angular refer to the broader concept of communication and event handling within the Angular framework.

Some key differences between Signals and RxJS:

  1. Abstraction Level: Signals in Angular are a broader concept that includes various mechanisms for communication between components, such as event emitters, template bindings, and services. RxJS, on the other hand, is a specific library focused on reactive programming and handling asynchronous data streams.
  2. Usage: RxJS is often used for handling complex asynchronous operations and data streams, whereas signals in Angular are more about component interaction, such as emitting events, listening to events, and reacting to changes within the Angular application.
  3. Flexibility: While RxJS provides powerful operators and tools for working with asynchronous data streams, signals in Angular provide a more structured way to handle component communication within the Angular framework.

Signal Equivalents of RxJS Maps

  • signalMergeMap(): signalMergeMap() would be similar to mergeMap() in RxJS. It would map each source signal to an inner signal and then merge the resulting signals into one. If a new source signal arrives before the previous inner signals complete, it will run them concurrently.
  • signalSwitchMap(): signalSwitchMap() would be akin to switchMap() in RxJS. It would map each source signal to an inner signal and switch to the latest inner signal whenever a new source signal arrives. This means that only the latest inner signal will be active, and previous inner signals will be unsubscribed.
  • signalConcatMap():signalConcatMap() would be comparable to concatMap() in RxJS. It would map each source signal to an inner signal and concatenate the resulting signals. This ensures that the inner signals are processed sequentially, with each inner signal completing before the next one starts.
  • signalExhaustMap(): signalExhaustMap() would resemble exhaustMap() in RxJS. It would map each source signal to an inner signal but would ignore new source signals if an inner signal is still active. It will only start mapping new source signals once the current inner signal completes.

Usage Example

Let's assume we have a sourceSignal$ observable representing incoming signals and a mapFunction that maps each signal to an inner signal. We can use these signal() functions as follows:

import { signalMergeMap, signalSwitchMap, signalConcatMap, signalExhaustMap } from 'rxjs-signal-maps';

// Using signalMergeMap()
sourceSignal$.pipe(
  signalMergeMap(signal => mapFunction(signal))
).subscribe();

// Using signalSwitchMap()
sourceSignal$.pipe(
  signalSwitchMap(signal => mapFunction(signal))
).subscribe();

// Using signalConcatMap()
sourceSignal$.pipe(
  signalConcatMap(signal => mapFunction(signal))
).subscribe();

// Using signalExhaustMap()
sourceSignal$.pipe(
  signalExhaustMap(signal => mapFunction(signal))
).subscribe();

Final Thoughts

In this conceptualization, we've introduced signalMergeMap(), signalSwitchMap(), signalConcatMap(), and signalExhaustMap() as conceptual equivalents of their RxJS counterparts. These functions provide different strategies for mapping source signals to inner signals, catering to various use cases such as concurrency control, sequential processing, and handling the latest value only. While they don't exist in RxJS, understanding their conceptual counterparts can deepen your understanding of reactive programming principles.

Lastly, when deciding whether to use signals or RxJS in your application, consider the complexity of your application, your familiarity with each approach, and the specific requirements of your project. It is recommended to choose signals in Angular for simpler component communication within Angular projects or when the requirements are relatively straightforward. Conversely, choose RxJS for more complex asynchronous operations, reactive programming needs, or when working on non-Angular projects where you need the advanced features provided by RxJS.

We hope you found this article informative. Please reach out with any questions or message us on Twitter or Linkedin.

Kim Pham

Senior Front-end Web Developer