d

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.

15 St Margarets, NY 10033
(+381) 11 123 4567
ouroffice@aware.com

 

KMF

Exploring Observables in JavaScript Programming

 One of the most powerful and popular JavaScript libraries that specializes in event processing is the Reactive Extensions for JavaScript library, or simply RxJS. RxJS uses the Gang of Four (GoF) design pattern named the Observable pattern as the basis for registering interest in an event, as well as doing something when an event has been triggered. 

This article is an excerpt from my book,‚ÄĮMastering TypeScript, 4th Edition¬†‚ÄĮ‚Äď A comprehensive guide to‚ÄĮunderstanding‚ÄĮTypeScript language and its latest features.‚ÄĮIn this¬†article, you will explore Observables in the¬†RxJS¬†library.¬†You can download the codes in this article¬†here.

The RxJS library provides a simple mechanism for registering interest in an event, as well as a mechanism for generating events. Along with these basic principles of the Observer design pattern, RxJS provides a plethora of utility functions to transform event data, as and when it comes in. At the heart of the RxJS library is the concept of Observables, which are source event streams. This has given rise to using the term Observables to describe RxJS source streams, and what can be done to them. So when someone says use Observables, they really mean use the RxJS library with its source streams and utility functions.  

Installing RxJS Library

To begin the discussion on Observables, let’s first install the RxJS library as follows: 

The RxJS library already includes the declaration files that are needed by TypeScript, so there is no need to install them separately using @types. 

To generate an Observable, we can use the of function as follows: 

import { of, Observable } from "rxjs";  

const emitter : Observable<number> = of(1, 2, 3, 4); 

Here, we start by importing the of function and the Observable type from the rxjs library. We are then defining a constant variable named emitter, which is using generic syntax to define its type as an Observable of type number. We then assign the result of the of function to the emitter variable, which will create an Observable from the numbers 1 through 4. We can now create an Observer as follows: 

emitter.subscribe((value: number) => { 
    console.log(`value: ${value}`) 
}); 

Here, we are calling the subscribe function on the variable emitter. As the emitter variable is of type Observable, it automatically exposes the subscribe function in order to register Observers. The subscribe function takes a function as a parameter, and this function will be called once for each value that is emitted by the Observable. The output of this code is as follows: 

value: 1 
value: 2 
value: 3 
value: 4 

Here, we can see that the function we passed into the subscribe function has indeed been called once for each value that is emitted by the Observable. 

Note that only when calling the subscribe function on an Observable will the Observable start to emit values. Calling the subscribe function is known as subscribing to an Observable, and the values that are produced by the Observable are also known as the Observable stream. 

The of function has a partner function named from, which uses an array as input into the Observable, as follows: 

const emitArray : Observable<number> = from([1, 2, 3, 4]); 
 
emitArray.subscribe((value: number) => { 
    console.log(`arr: ${value}`); 
}); 

Here, we have a variable named emitArray, which is of type Observable<number>, and is using the from function to create an Observable out of an array. Again, we call the subscribe function on the Observable named emitArray, and provide a function to be called for each value emitted by the Observable. The output of this code is as follows: 

arr: 1 
arr: 2 
arr: 3 
arr: 4 

Here, we can see that the from function has created an Observable stream from the array input, and that the function we provided to the subscribe function is being called once for each value that is emitted by the Observable. 

Pipe and Map 

The RxJS library provides a pipe function to all Observables, similar to the subscribe function. This pipe function takes a variable number of functions as parameters and will execute these functions on each value that is emitted by the Observable. The functions that are provided to the pipe function are generally known as Observable operators, which all accept an Observable as input, and return an Observable as output. The pipe function emits an Observable stream. 

This concept is best explained by reading some code, as in the following example: 

import { map } from "rxjs/operators"; 
const emitter = of(1, 2, 3, 4); 
 
const modulus = emitter.pipe( 
    map((value: number) => { 
        console.log(`received : ${value}`); 
        return value % 2; 
    })); 
 
modulus.subscribe((value: number) => { 
    console.log(`modulus : ${value}`); 
}); 

Here, we start with an Observable named emitter, which will emit the values 1 through 4. We then define a variable named modulus to hold the results of calling the pipe function on the emitter Observable. The only argument we are providing to the pipe function is a call to the map function, which is one of RxJS’ operator functions. 

The map function takes a single function as a parameter and will call this function for each value that is emitted by the Observable. The map function is used to map one value to another, or to modify the value emitted in some way. In this sample, we are returning the result of applying the modulus of two to each value. 

Finally, we subscribe to the Observable and log its value to the console. The output of this code is as follows: 

received : 1 
modulus : 1 
received : 2 
modulus : 0 
received : 3 
modulus : 1 
received : 4 
modulus : 0 

Here, we can see that emitter Observable emits the values one through four, and that the modulus Observable is emitting the modulus of 2 for each value received.  

Note that in these code samples, we have not explicitly set the type for our Observables. 

The emitter Observable and the modulus Observable could be explicitly typed as follows: 

const emitter : Observable<number> = of(1, 2, 3, 4); 
 
const modulus : Observable<number> = emitter.pipe(  
    ... 
); 

Here, we have specified the type of both the emitter Observable, and the modulus Observable. This is not strictly necessary, as the TypeScript compiler will determine the correct return types when working with Observables. It does, however, explicitly state what we are expecting out of the Observable stream, and in larger, or more complex Observable transformations, explicitly setting the expected return type makes the code more readable and can prevent errors. 

Combining Operators 

The pipe function allows us to combine multiple operator functions, which will each be be applied to the values emitted by an Observable. Consider the following code: 

const emitter = of(1, 2, 3, 4); 
 
const stringMap = emitter.pipe( 
    map((value: number) => { return value * 2 }), 
    map((value: number) => { return `str_${value}` }) 
); 
 
stringMap.subscribe((value: string) => { 
    console.log(`stringMap emitted : ${value}`); 
}); 

Here, we have an Observable named emitter that will emit the values 1 through 4. We then have a variable named stringMap that holds the result of the pipe function on the emitter Observable. Within this pipe function, we have two map functions. The first map function will multiply the incoming numeric value by 2, and the second map function will convert it to a string, with the prefix str_. 

We then subscribe to the Observable and log each value to the console. The output of this code is as follows: 

stringMap emitted : str_2 
stringMap emitted : str_4 
stringMap emitted : str_6 
stringMap emitted : str_8 

Here, we can see that both map functions have been applied to each value emitted by the emitter Observable. Note that we have actually modified the type of each value from type number to type string, in our second map function. This is why the type specified for the value parameter in our subscribe function is of type string. 

Summary 

In this article, we have explored the basics of the RxJS library and the fundamental concept of Observables that it provides. We have seen how we can create Observables easily using the of and from functions. Learn more in the book Mastering TypeScript, Fourth Edition.

About the Author

Nathan¬†Rozentals¬†has been writing commercial software for over 30 years, in C, C++, Java and C#. He picked up TypeScript within a week after its initial release in October 2012 and realized how much TypeScript could help when writing JavaScript.‚ÄĮ‚ÄĮ‚ÄĮ¬†

Credit: Source link

Previous Next
Close
Test Caption
Test Description goes like this