JavaScript Reactive/Asynchronous Code with RxJS 6, TypeScript & Angular 10: Callbacks, Promises and Observables

JavaScript Reactive/Asynchronous Code with RxJS 6, TypeScript & Angular 10: Callbacks, Promises and Observables

Throughout this tutorial, you'll be introduced to JavaScript reactive and asynchronous code, data streams and RxJS 6 used in TypeScript and Angular.

You'll learn that reactive programming in JavaScript and TypeScript is about coding with asynchronous data streams and that RxJS is the most popular JavaScript implementation that implements Observables and the observer pattern.

RxJS is included by default in Angular 2+ which is based on TypeScript instead of plain JavaScript.

You'll learne about RxJS operators, the methods that are used to compose Observables and work on their data streams.

Next, you'll learn that Angular 10/9 uses RxJS v6 for working with asynchronous operations and APIs (instead of TypeScript callbacks and Promises) in many of its commonly used modules such as HttpClient, Router and Reactive Forms.

Reactive eXtensions for JavaScript with Angular 10

We'll learn to use the RxJS 6 library with Angular 10/9 and TypeScript. We'll learn about:

  • How to import the Observable class and the other operators in TypeScript.
  • How to subscribe and unsubscribe from Observables.
  • How to import and call operators and chain them with the pipe() function in TypeScript.
  • We'll also see how to use the async pipe to subscribe to Observables from Angular 10 templates.
  • Finally we'll see how to use some popular pipeable operators such as tap(), map() and filter() and their new import paths in RxJS 6.

Note: This tutorial works with both Angular 10 and the previous versions of Angular.

This tutorial is divided in two parts. You can check out the second tutorial from this link.

We’ll start looking at what reactive programming, asynchronous operations and data streams are and how they are related to the RxJS library. We’ll then see the concept of an RxJS Observable with examples, the various types of Observables such as:

Next, we’ll see what RxJS operators are and examples of some popular operators such as tap(), map(), filter(), share(), etc. And finally we’ll see how Angular 10 and TypeScript uses the RxJS Observable to do asynchronous programming.

What is Reactive Programming in JavaScript/TypeScript?

What is Reactive Programming

Let’s see the definition of Reactive programming from different sources.

This is how  Andre Staltz, the creator of cycle.js (A functional and reactive JavaScript framework for predictable code) defines it:

Reactive Programming is programming with asynchronous data streams 

This means when you are writing code that deals with asynchronous operations and streams of data, you are doing reactive programming.

Now, this is the definition from Wikipedia which is more in-depth:

In computing, reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change.

This means reactive programming is a declarative (vs. a procedural) style of programming that works on streams of data.

For a detailed guide on reactive programming and data streams, check out: The introduction to Reactive Programming you've been missing.

What is a Stream in JavaScript Reactive Programming?

A stream is an essential concept in reactive programming so it's worth seeing the definition before we proceed further.

What is a stream

In all definitions we’ve seen the word stream.

So what is a stream?

Simply put:

A stream refers to values of data overtime.

We'll see later that Observables and streams are very related concepts.

Introducing the RxJS Library (Reactive Extensions for JavaScript)

Now, that we’ve seen the conceps of reactive programming and data streams, let’s see what's the JavaScript RxJS library.

What is RxJS

RxJS is a popular library among web developers. It provides functional and reactive programming patterns for working with events and streams of data and has been integrated in many web development libraries and frameworks such as TypeScript and Angular.

RxJS makes it easy for JavaScript developers to write asynchronous code using composable Observables instead of callbacks and Promises.

RxJS stands for Reactive Extensions for JavaScript and it actually has implementations in other programming languages such as Java, Python, Ruby, and PHP etc. Can be used in TypeScript and It's also available for platforms such as Android. Check out the complete list of supported languages and platforms.

RxJS v6 is currently the stable version of RxJS and it has many breaking changes with RxJS v5. You can check out more information about the changes and how to migrate from the old version from this official migration guide.

RxJS 6 has many advantages over the previous RxJS 5 version(s), such as:

  • The bundle size of the library is smaller,
  • The performance of the latest version is better,
  • RxJS 6 Observable follows the Observable Spec Proposal,
  • The latest version provides better debugability,
  • A better modular architecture,
  • It's backward compatible.

How to Install and Use Reactive Extensions for JavaScript Library?

RxJS is a JavaScript library which means you can install it in the same way you install other libraries.

Using Reactive Extensions for JavaScript Using ES6 via npm

In your project, you can run the following command to install RxJS:

$ npm install rxjs

You can then import the symbols you want to use from the rxjs package or a sub-package such as rxjs/operators:

import { Observable, Subscriber } from 'rxjs';
import { tap, map, filter } from 'rxjs/operators';

We imported the Observable and Subscriber symbols from rxjs and the tap, map and filter operators from rxjs/operators.

We'll see later what these symbols are and how to use them in your Angular 10 application.

Using Reactive Extensions for JavaScript from a CDN

You can also use RxJS from a CDN using a <script> in your HTML document:

<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>

Note: Please note that in Angular 10/9, RxJS 6 is already included in your project so you don't need to install it manually.

What is an Observable, Observer and Subsription in RxJS

RxJS uses the concept of Observables to handle and work with asynchronous and event-based code.

The asynchronous word comes from Asynchrony. In computer programming, here is the definition of Asynchrony from Wikipedia:

Asynchrony, in computer programming, refers to the occurrence of events independent of the main program flow and ways to deal with such events. These may be "outside" events such as the arrival of signals, or actions instigated by a program that take place concurrently with program execution, without the program blocking to wait for results.

After reading this definition, you may have concluded how much asynchrony is important for computers and programming!

Let's make this simple!

Asynchronous code is the inverse of synchronous code which is the original way of thinking about your code when you are first introduced to programming.

Your JavaScript code is synchronous when it's running in sequences i.e instruction by instruction in the order they appear in the source code.

Example Synchronous TypeScript/JavaScript Code

For example, let's consider this simple JavaScript code:

const foo = "foo" //1
const bar = "bar" //2
const foobar = foo  +  bar //3
console.log(foobar) //4

The browser will run this synchronous code line by line from line 1 to 4 starting by assigning the foo and bar variables, concatenating them and displaying the foobar variable in the console.

Note: If your code is authored in TypeScript, it needs to be compiled to JavaScript before it can be executed by web browsers.

JavaScript and TypeScript Supports the Asynchronous Way..

JavaScript and TypeScript support also the asynchronous approach of writing code which makes sense, since you need to respond to the user events in the browser but you don't actually know when the user interacts with your application (and in which order) when you are writing code.

This was originally achieved using callbacks which you need to define in your JavaScript and TypeScript code and specify when they will be called.

For example, the following JavaScript asynchronous code will display You clicked the button! when the user clicks the button identified by the mybutton identifier:

document.getElementById('mybutton').addEventListener('click', () => {
  console.log("You clicked the button!")
})

The second argument of the addEventListener() method is the callback.

Handling Asynchronous Code in JavaScript and TypeScript Using Callbacks

You can also use callbacks to handle asynchronous operations in JavaScript and TypeScript which don't involve the DOM. For example, the following code can be used to send an HTTP POST request to a web server:

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
  if (xhr.readyState === 4) {
    xhr.status === 200 ? console.log(xhr.responseText) : console.error('error')
  }
}
xhr.open('POST', 'your.server.com')
xhr.send()

This is how you perform the famous Ajax calls in JavaScript.

Actually, Ajax itself stands for Asynchronous JavaScript and XML.

Note: Sending HTTP requests (which is a common operation in web apps) is an asynchronous operation by nature since the request will take time to reach the server which will then send a response back to your client application. In this mean time, the application needs to respond to other actions and perform other tasks and only process the server response when it's received.

If you have ever extensively worked with callbacks, you'll notice one problem with them. They are difficult to track!

When you write complex applications you usually end up writing nested callbacks (callbacks inside callbacks) with multiple nesting levels. This is what's known as the callback hell.

Using Promises and Async/Await Syntax for Handling JavaScript/TypeScript Asynchronous Code

Modern JavaScript and TypeScript introduced other approaches or abstractions to deal with asynchronous operations (without using too much callbacks) such as Promises and Async/Await.

Promises have been introduced in ES6 (JS 2015).

Async/await has been introduced in ES8 (JS 2017) and it's actually a syntactic sugar on top of Promises which helps developers write asynchronous code with Promises in a way that looks synchronous.

See how to use the async/await syntax in TypeScript

But Promises are actually similar to callbacks and have the same nesting problem at some degree.

Using Observables for Handling JavaScript and TypeScript Asynchronous Code

Since developers are always looking for better solutions we now have Observables which use the observer software pattern.

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. Observer pattern.

Observables are implemented in the ReactiveX project which has implementations in various languages. RxJS is the JavaScript implementation which can also be used with TypeScript.

Note: Observables are implemented in many other libraries such as zen-observable and xstream but RxJS Observables are the most popular in JavaScript.

Observables are not yet a builtin feature of JavaScript but there is a proposal to add them in EcmaScript.

See how to convert a TypeScript promise to an RxJS Observable/Subject

RxJS Operators

RxJS provides the implemenation of Observable concept but also a variety of operators that allows you to compose Observables.

Operators offer a declarative way to perform complex asynchronous operations with Observables.

An operator works on a source Observable by observing its emitted values and applying the intended transformation on them then return a new Observable with the modified values.

There many RxJS operators such as:

  • tap(),
  • map(),
  • filter(),
  • concat(),
  • share(),
  • retry(),
  • catchError(),
  • switchMap(),
  • and flatMap() etc.

RxJS Pipes: Combining Multiple Operators

RxJS provides two versions of the pipe() function: A standalone function and a method on the Observable interface.

You can use the pipe() function/method to combine multiple Operators. For example:

import { filter, map } from 'rxjs/operators';
const squareOf2 = of(1, 2, 3, 4, 5,6)
  .pipe(
    filter(num => num % 2 === 0),
    map(num => num * num)
  );
squareOf2.subscribe( (num) => console.log(num));

The of() method will create and return an Observable from the 1, 2, 3, 4, 5,6 numbers and the pipe() method will apply the filter() and map() operators on each emitted value.

Using the RxJS map() Operator

The map() operator is similar to the Array.map() method. It lets you map observable responses to other values. For example:

import { Observable} from 'rxjs';
import { map } from 'rxjs/operators';
getItems(): Observable> {
  return this.aService.getItems().pipe(map(response => response.data));
}

The getItems() method returns an Observable. We're using the map() operator to return the data property of the response object.

The operator enables us to map the response of the Observable stream to the data value.

We import the pipeable operator map() from the rxjs/operators package and we use the pipe() method (which takes a variable number of pipeable operators) to wrap the operator.

Using the RxJS filter() Operator

The filter() operator is similar to the Array.filter() method. It lets you filter the observable stream and returns another observable. For example:

import { Observable} from 'rxjs';
import { filter } from 'rxjs/operators';

filter(): Observable<Array<any>> {

  return this.aService.getItems()
    .pipe(
      filter(response => response.code === 200));
}

We use the filter() operator to only emit a notification to observers of the observable stream when the status code of the HTTP response is 200.

Check out the second part.

Conlusion

In this tutorial, you have been introduced to reactive programming, data streams and RxJS 6 in both JavaScript and TypeScript.

You have learned that reactive programming is about coding with asynchronous data streams and that RxJS is the most popular implementation that implements Observables and the observer pattern.

You learned about RxJS operators which are methods that are used to compose Observables and work on their data streams.

Finally, you learned that TypeScript/Angular 10/9 uses RxJS v6 for working with asynchronous operations and APIs (instead of callbacks and Promises) in many of its commonly used modules such as HttpClient, Router and ReactiveForms.



✋If you have any questions about this article, ask them in our GitHub Discussions 👈 community. You can also Gitter

✋ Want to master Angular 14? Read our angular tutorial and join our #DailyAngularChallenge where we learn to build components, directives, services, pipes and complete web, mobile, and desktop applications with latest Angular version.

✋ Make sure to join our Angular 14 Dev Community 👈 to discuss anything related to Angular development.

❤️ Like our page and subscribe to our feed for updates!

Find a list of emojis to copy and paste