Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

August 19, 2019

What Is a Higher-Order Observable?

Filed under: Angular,RxJS @ 12:11 pm
Tags: ,

One of the challenges of diving deeper into RxJS, or Reactive Extensions for JavaScript, is the terminology. We may know the basics of using RxJS, but to really understand how it works, we need a firm grasp of its vocabulary.

Let’s focus on answering these questions:

  • What *is* a higher-order Observable?
  • What are inner and outer Observables?
  • And what is the purpose of a higher-order mapping operator, such as switchMap?

By mastering these concepts, we can better leverage RxJS to build more declarative and reactive Angular applications.

Background

From the RxJS documentation at rxjs.dev:

RxJS is a library for reactive programming using Observables, to make it easier to compose asynchronous or callback-based code.”

With RxJS, we work with any stream of data in a consistent manner. The data can be simple types, such as numbers or strings; or complex types, such as an array of customers or messages. It can be the response returned from an HTTP request. The stream can come from user input such as mouse or keyboard events.  We can use RxJS to manage a stream of anything.

When talking about Observables, the terms Observable stream, Observable, or just stream all reference the same thing: the collection of data items.  Those data items can emit immediately (synchronously) or over time (asynchronously). The stream can emit a finite number of items, such as a set of numbers, or an infinite number of items, such as a timer ticking every 1000 milliseconds forever.

Observables are lazy, meaning they don’t emit any items until we subscribe to them. Observables continue to emit values until the stream completes, an error occurs, or we unsubscribe.

Working with a single stream of data is just the beginning. With RxJS we can collect and combine data from multiple sources and transform that data as needed for the user. We can cache data in the application to minimize hits to our backend server. We can easily react to user actions and state changes. We can compose all the streams for a view into one stream to simplify complex UIs. To get this power from RxJS, we need to understand its terminology.

Here is a simple example of an Observable. It uses the `of` creation function (static method) to define an Observable stream. When subscribed, the stream emits three numbers and completes. This is represented as a marble diagram that depicts each emitted item as a marble. The straight vertical line at the end represents the complete notification.

 

When working with a response from an Http request, your Observable may look more like this:

 

In this example, the HTTP get request returns an Observable that emits the HTTP response: an array of three products. The Observable then completes. I often refer to these types of Observables as “data Observables” because their purpose is to emit the data from an API endpoint via HTTP.

These are all examples of simple Observables. More formally, they can be called “first order Observables”. So what is a higher-order Observable?

Higher-Order Observables

A higher-order Observable is an Observable that emits other Observables. “Why?” you may ask.

Let’s answer that with an example. Say we retrieve product data and as part of that data we get a set of supplier ids. The users want to see the list of suppliers along with the product so they can quickly reorder when inventory is low. So given the supplier ids, we need to retrieve each supplier’s data.

Our first thought might be something like this:

 

The first Observable uses the `of` creation function to define an Observable stream with two numbers. This is the outer Observable. When each number is emitted, that number is mapped to an http get request that returns an Observable. This is the inner Observable. So we now have an Observable that emits other Observables. This is a higher-order Observable. We define a higher-order Observable whenever we use data emitted from one Observable to emit another Observable.

But, this code does not actually work. Do you see why?

In the code above, we subscribe to the outer Observable but we don’t subscribe to the inner Observable. Since Observables are lazy, they won’t emit until subscribed. So, despite the pretty marble diagram, our inner Observables will never actually emit. How do we fix that?

One thought might be something like this:

In the above code, we issue an Http get request to get a product’s data. This request returns our outer Observable. We subscribe to that Observable at the bottom of this code. These are marked in orange.

We process the product data within the outer Observable’s pipeline, defined with the pipe method. There, we use the `tap` operator and loop through each supplier id for the product, issuing an http get request to get the supplier data. This request returns an inner Observable for each supplier, which we subscribe to as shown in green.

This results in nested subscribes, which are problematic for several reasons. Looking at the above code, you can see how challenging it could be to unsubscribe from these Observables. It would be difficult to make this code more declarative. And how would this work with an async pipe?

This is where higher-order mapping operators can help.

Higher-Order Mapping Operators

High-order mapping operators transform higher-order Observables. They map each value from an outer Observable to a new inner Observable and automatically subscribe to, and unsubscribe from that inner Observable.

RxJS provides several higher-order mapping operators. You can recognize a higher-order mapping operator by its name, which ends in `map`, such as mergeMap and switchMap.

Let’s look at a few of them.

concatMap

The concatMap higher-order mapping operator *waits* for each inner Observable to complete before processing the next one. It concatenates the results of the inner Observables in sequence.

Here, the outer Observable emits two numbers and for each emitted number it calls http get. When the first number is emitted, the concatMap automatically subscribes to the first inner Observable and the http get request is issued. It then *waits* for the returned response. Once the first inner Observable completes, it emits the result to the result stream. Only then does it subscribe to the next inner Observable, issuing that get request. It again waits for the returned response. When all inner Observables complete, the result stream completes.

Use concatMap if you want to ensure that each inner Observable is processed one at a time and in order. This is a great technique to use when updating or deleting data to ensure each operation is processed in sequence.

mergeMap

The mergeMap higher-order mapping operator processes each inner Observable in parallel. It merges the results of the inner Observables as they complete.

This is the same example, but with mergeMap. The outer Observable emits two numbers and for each emitted number it calls http get. When each number is emitted, the mergeMap automatically subscribes to its inner Observable and issues each http get request. As each response is received, it is merged to the result stream. When all inner Observables complete, the result stream completes.

Because the inner Observables are processed in parallel, the result stream may not be in sequential order. If, in the above example, the http get request for supplier s3 took longer to execute, its result could be emitted to the result stream after supplier s7.

Use mergeMap for better performance since each inner Observable is executed concurrently, but only if the resulting order doesn’t matter.

switchMap

The switchMap higher-order mapping operator unsubscribes from any prior inner Observable and switches to any new inner Observable.

Again, using the same example but with switchMap. The outer Observable emits two numbers and for each emitted number it calls http get. When the first number is emitted, the switchMap automatically subscribes to the first inner Observable and the http get request is issued. When the next item is emitted, switchMap unsubscribes from the prior inner Observable and switches to the new inner Observable, subscribing to it. If an inner Observable completes, it emits the result to the result stream. When all inner Observables complete, the result stream completes.

Use switchMap to stop any prior inner Observable before processing the next one. This is useful for features such as type ahead or autocompletion when you want to stop prior processing when the user types the next character.

No More Nested Subscribes

Any time we use data emitted from an Observable to emit another Observable, we need to subscribe to both the source outer Observable and the emitted inner Observable. Instead of nested subscribes, we use higher-order mapping operators. Higher-order mapping operators map higher-order Observables and take care of subscribing and unsubscribing to the inner Observables so we don’t have to.

For More Information

To learn more about RxJS, check out my Pluralsight course: “RxJS in Angular: Reactive Development”. You can find it at this URL: https://app.pluralsight.com/library/courses/rxjs-angular-reactive-development

The “RxJS in Angular” course starts with key terms such as observer, Observable, and subscriber and describes how Observable operators work. It covers reactive patterns for collecting data using RxJS. It then dives deeper looking at several techniques for combining streams from multiple data sources and mapping the returned data to a user-friendly format. It demonstrates how to react to user actions by creating action streams using Subject and BehaviorSubject. It examines how to cache streams to minimize hits to the server. It walks through several higher-order mapping operators. And it demonstrates how to combine all the streams for a view to provide the detailed information users require.

To sign up for a free Pluralsight trial, check out this link: https://www.pluralsight.com/pricing/free-trial

RSS feed for comments on this post. TrackBack URI

Sorry, the comment form is closed at this time.

© 2019 Deborah's Developer MindScape   Provided by WPMU DEV -The WordPress Experts   Hosted by Microsoft MVPs