Kattie  Quitzon

Kattie Quitzon

1637946420

New Angular Course Interviews

I am thrilled to announce my new #Angular Course which is designed to help you with preparation for Angular interviews and increase your chances to get the desired job. This course is a constantly growing database of the most popular Angular interview questions and detailed answers on it.

鈿f you want to take part in the contest, here is what you have to do:
1. Leave any feedback under this video;
2. Share one of my videos you like the most on either Twitter, LinkedIn, or Facebook;

On 5 November 2021, during the live stream, there will be randomly picked 5 people who will get the course which includes Mock-Interview + Feedback for FREE 馃帀

馃晵 Time Codes:
00:00:00 - Intro;
00:00:15 - New Course Promo ;
00:02:04 - Price Models;
00:03:24 - Context and Rules;

#webdevelopers  #typescript  #rxjs

  New Angular Course Interviews

Unsubscribe an Observable in An Angular Application

Hi everyone,

Video about - Why We Have To Unsubscribe An Observable In An Angular Application?

#angular  #rxjs 

Unsubscribe an Observable in An Angular Application

How to Get Previous Current Values with Rxjs

Rxjs how to get previous current values is what we will explore today. I will provide you with the solution that I used this week in my day to day work.

With rxjs you can combine many operators also when you need to get previous and current values out of a stream is this the solution.

#rxjs 

How to Get Previous Current Values with Rxjs
Sherman  Zemlak

Sherman Zemlak

1636651980

A Full Stack Course App Demo Spring Boot and Angular with RxJS

Learn advanced Spring Boot API and asynchronous programming with Angular and RxJS

#spring #angular #rxjs 

A  Full Stack Course App Demo Spring Boot and Angular with RxJS

C贸mo Usar El Algoritmo Compartir En Observables Usando angular

Curso de Angular con RxJS (ReactiveX) donde comentamos el uso del operador Share en Observables. A partir de Angular 2.

#angular  #rxjs 

C贸mo Usar El Algoritmo Compartir En Observables Usando angular

C贸mo Usar El Operador Concat En Observables Usando angular

Curso de Angular con RxJS (ReactiveX) donde comentamos el uso del operador Concat en Observables. A partir de Angular 2.

#angular  #rxjs 

C贸mo Usar El Operador Concat En Observables Usando angular

Una Breve Gu铆a De Los Ciclos De RxJS En angular

Breve tutorial sobre el Ciclo de RxJS (ReactiveX) en Angular de una Observable (Next, Error y Complete).

#rxjs #angular 

Una Breve Gu铆a De Los Ciclos De RxJS En angular
Lamar  Schmeler

Lamar Schmeler

1636522500

RxJs SwitchMap vs MergeMap

In this video, we are going to start learning of flattening operators in #rxjs, namely MergeMap (former FlatMap) and SwitchMap. We will see how data flows in flattening operators and we will visualize it using mental models from real life.

馃晵 Time Codes:
00:00:00 - Intro;
00:00:52 - Mental model for mergeMap & switchMap;
00:06:11 - Let's code it!;
00:24:47 - Outro;

 #angular  #webdevelopers 

RxJs SwitchMap vs MergeMap

Tutorial Del Operador De Tiempo De B煤fer De ReactiveX En # angular

Breve tutorial sobre el operador bufferTime de ReactiveX (RxJS) en Angular (a partir de Angular 2). Para este caso utilizamos un entorno de Angular 7 con RxJS 6+ (funciona tambi茅n en un entorno con Angular 8).

#angular #rxjs 

Tutorial Del Operador De Tiempo De B煤fer De ReactiveX En # angular

C贸mo Usar El Operador SwitchMap En RxJS En angular

Tutorial sobre el uso del operador switchMap en #RxJS (ReactiveX) en #Angular. Dura m谩s de lo habitual debido a que lo explicamos con dos ejemplos, uno muy b谩sico y uno de los t铆picamente llamados "Ejemplo de la Vida Real" (Real life example). Para ver los puntos m谩s importantes, les dejamos los siguientes minutos:

#rxjs #angular 

C贸mo Usar El Operador SwitchMap En RxJS En angular

How to Call RESTful APIs - Web Services using Angular and RxJS

Call RESTful APIs | Web Services using Angular and RxJS.

Call and cache data returned from HttpClient. Caching data will speed up your web/app. Let's understand how!

HttpClient returned an observable. To cache, we need to combine that observable with shareReplay and catchError. We use caching to avoid too many requests to the server.

Let鈥檚 dive into the details of HTTP API calls:

1. Without using Caching.
2. With Caching values.
3. With Caching Observable.

#api #angular #rxjs #javascript 

How to Call RESTful APIs - Web Services using Angular and RxJS

Explore Flattening RxJs Operators ExhaustMap vs ConcatMap

Today we continue to explore flattening rxjs operators and namely ExhaustMap vs ConcatMap. We will have a look at how different the behavior of both operators is, see examples, mental models, and many more. I hope you will like the video and find it useful!

馃晵 Time Codes:
00:00:00 - Intro;
00:00:35 - Mental model for concatMap & exhaustMap;
00:03:25 - Let's code it!;
00:10:43 - Outro;

Subscribe: https://www.youtube.com/c/DecodedFrontend/featured 

#rxjs  #angular  #webdevelopement 

Explore Flattening RxJs Operators ExhaustMap vs ConcatMap
Kattie  Quitzon

Kattie Quitzon

1633499640

Real-Life Analog of RxJs ZIP Operator

In this video, we continue to look for real-life analogs for #rxjs operators, and today we are going to look at the ZIP operator. This operator allows you to combine multiple streams together and emit an array of values arrived from combined streams. Additionally, this operator cares about the order of how values should be paired (it is hard to explain it with text - better one time to see). I hope you will find this video useful. Stay tuned and enjoy watching!

馃晵 Time Codes:
00:00:00 - Intro;
00:00:39 - Mental model for ZIP operator;
00:02:56 - Let's code it!;
00:13:20 - Outro;

#rxjs  #angular  #webdevelopers 

Real-Life Analog of RxJs ZIP Operator
Joel  Hawkins

Joel Hawkins

1633315975

Everything You Need to Know About RxJS

This article to show the basics behind the RxJS library and Reactive Programming in general.

RxJS is a library for composing asynchronous and event-based programs by using observable sequences. So RxJS and Reactive Programming are not the solution for all your problems, but they are good solutions in contexts where asynchronous and events are the stars. I've preferred to clarify that because sometimes programmers believe that but after having introduced the Reactive Programming, they come across with other problems because the library and the paradigm are very simple but at the same time it's easy to get hurt if you don't know how it works.

The code is written in Typescript, but you can use RxJS also in vanilla JavaScript if you prefer.

In this tutorial, we'll learn:

  1. Observable
  2. Observer and Subscription
  3. Operators
  4. Marble Diagrams
  5. Creation Operators
  6. Filtering Operators
  7. Transformation Operators
  8. Join Operators
  9. Utility Operators
  10. Conditional & Mathematical Operators
  11. Error Handling Operators
  12. Multicast Operator

1 - Observable

Observables (the one who is observed) are one of the key concepts behind Reactive Programming along with Observers and Subscribers (those who observes, controls).
Observables can be a stream or a collection of datas, but you could imagine an observable like a lazy Push collection of multiple values.
To understand better the concept let me show you an example

import { Observable, Subscriber } from "rxjs";

const observable = new Observable<string>((subscriber: Subscriber<string>) => {
  subscriber.next("Hello World");
  subscriber.error(new Error("Something went wrong!"));
  subscriber.complete();
});

As you can see the Observable is a class that accept a subscriber (a callback function).
This subscriber has 3 main possible action:

  1. next: The next method emits the value passing as parameter to all the subscriptions, so the system can react accordingly.
  2. error: The error method emits an error during the execution of the observable.
  3. complete: The complete method sets the observable closed. When this happens, all the future methods (next and error) emitted for the closed observable will be ignored.

To better understand here's another example, with its result in the console

import { Observable, Observer, Subscriber } from "rxjs";

const observer: Observer<string> = {
  next: (value: string) => console.log("next", value),
  error: (error: Error) => console.error("error", error),
  complete: () => console.log("complete!"),
};

const observable = new Observable<string>((subscriber: Subscriber<string>) => {
  subscriber.next("Hello");
  subscriber.next("World");

  subscriber.complete();

  // this will never be logged

  subscriber.error(new Error("Something went wrong!"));

  subscriber.next("Hello");
  subscriber.next("World");
});

observable.subscribe(observer);
next Hello
next World
complete!

p.s. don't pay attention to the observer in this moment, to simplify it think that when the subscriber calls the next method, the next function in the observer will be called and the same goes for the error and complete methods

You can notice how the subscriber calls the next method twice: first with "Hello" and after with "World" and the result is logged into the console. Next, the subscriber calls the complete method and it is registered in the console too. After that, the subscriber calls the error method and the next method twice, but in the console nothing happens. This behaviour is due to the fact that the observable is ended by the complete method so the observable no longer emits any events.
When we complete the observable, it's important to remember that all the next methods called (next, error or complete) are ignored.


2 - Observer and Subscription

An observer is a consumer of values. An observer is something that is interested to the emitted values by the observable. In RxJS an Observer is simply a set of callbacks (next, error, complete).
Here's a simple example of an observer

import { Observer } from "rxjs";

const observer: Observer<string> = {
  next: (value: string) =>
    console.log(`[observer] next`, value),
  error: (error: Error) =>
    console.error(`[observer] error`, error),
  complete: () =>
    console.log(`[observer] complete!`),
};

As you can see an observer has three callbacks, one for each type of notification that an Observable may emit. Every callback can react in the base of the observable event notification.
An observer could have not all the three callbacks defined because maybe it doesn't want to react to all the notifications. In these cases, you can create an observer with the only callbacks you need.

import {
  NextObserver,
  ErrorObserver,
  CompletionObserver,
} from "rxjs";

const nextObserver: NextObserver<string> = {
  next: (value: string) =>
    console.log(`[nextObserver] next`, value),
};

const errorObserver: ErrorObserver<string> = {
  error: (error: Error) =>
    console.error(`[errorObserver] error`, error),
};

const completeObserver: CompletionObserver<string> = {
  complete: () =>
    console.log(`[completeObserver] complete!`),
};

It's possible to define a NextObserver without the next property but indicating only the body of the next method, in this case, the observer by default is of the type NextObserver.
 

const defaultNextObserver: (value: string) => void = (value: string) =>
    console.log(`${new Date().toISOString()} - [defaultNextObserver] next`, value)

Subscription

A Subscription is an actor that decides when an Observable must be listened and when we can stop listening to it.
Until an observable is not subscribed in your code nothing happens, but as soon as you create a subscription, the magic starts.
A Subscription in RxJS is an object created using the method "subscribe" and it has one main method: "unsubscribe"; this method allows you to stop listening the observable event.
In your code is important to call the "unsubscribe" when you no longer need the subscription, this prevent problems as the memory leaks.
An example of a Subscription

import { Subscription } from "rxjs";

const observer = (value: string) => console.log(`[unsubscribe method] next`, value)

const subscription: Subscription = observable.subscribe(observer);
subscription.unsubscribe();

Another pretty feature in RxJS Subscription is the "add" method; this method allows you to collect more subscriptions inside of one Subscription instance and after that, you can unsubscribe all the subscriptions at once time.

import { Subscription } from "rxjs";

const subscription: Subscription = observable.subscribe((value: string) =>
  console.log(
    `[unsubscribe 1 method] next`,
    value
  )
);
subscription.add(
  observable.subscribe((value: string) =>
    console.log(
      `[unsubscribe 2 method] next`,
      value
    )
  )
);

subscription.unsubscribe();

In some cases when you call the unsubscribe method you need to run some special code in your observable: RxJS let us do this using a special syntax inside of the observable declaration. When you create the observable you can return a function that the library invokes in the future during the unsubscription. Below a simple example to understand better the problem and the solution:

import {
  Observable,
  Subscriber,
  Subscription,
} from "rxjs";

const observableWithCallback = new Observable<string>(
  (subscriber: Subscriber<string>) => {
    let count = 0;
    const id = setInterval(() => {
      subscriber.next(`Count: ${++count}`);
    }, 300);

    return () => {
      console.log("On UnSubscription");
      clearInterval(id);
    };
  }
);

const subscriptionObservableWithCallback: Subscription = observableWithCallback.subscribe({
  next: (value: string) =>
    console.log(`[observableWithCallback] Next: ${value}`),
});
setTimeout(() => {
  subscriptionObservableWithCallback.unsubscribe();
}, 3000);
[observableWithCallback] Next: Count: 1
[observableWithCallback] Next: Count: 2
[observableWithCallback] Next: Count: 3
[observableWithCallback] Next: Count: 4
[observableWithCallback] Next: Count: 5
[observableWithCallback] Next: Count: 6
[observableWithCallback] Next: Count: 7
[observableWithCallback] Next: Count: 8
[observableWithCallback] Next: Count: 9
On UnSubscription

3 - Operators

Operators are functions of two types in RxJS: Creation or Pipeable.

Creation

Creation operators are simple functions and their scope is to create new observables.

import { of } from "rxjs";

of(1, 2, 3, 4).subscribe(x => console.log("[of] result", x));
[of] result 1
[of] result 2
[of] result 3
[of] result 4

The most commons creation operators are: ajax, empty, from, fromEvent, interval, of, throwError, timer, combineLatest, concat, forkJoin, merge and zip.

Pipeable

Pipeable operators are functions that take an Observable as input and return another observable. These functions are pure, so the input observable does not change but the function returns a new one. The main scopes of these operators are: transform, filter, and work with the input observable.
An example of pipeable operators:

import { Observable } from "rxjs";
import { map, tap } from "rxjs/operators";

new Observable<number>(observer => {
  observer.next(1);
  observer.next(2);
  observer.next(3);
  observer.next(4);

  observer.complete();
})
  .pipe(
    map(val => val * 2),
    tap(res => {
      console.log("[pipeable tap]", res);
    })
  )
  .subscribe();
[pipeable tap] 2
[pipeable tap] 4
[pipeable tap] 6
[pipeable tap] 8

The pipeable operators are divided by scopes and the scopes are: Transformation, Filtering, Join, Multicasting, Error Handling, Utility, Conditional and Boolean and Mathematical and Aggregate.

As you can understand you can combine Creation operators with Pipeable operators to manage your business logic like here

import { timer } from "rxjs";
import { take } from "rxjs/operators";

timer(0, 1000)
  .pipe(take(10))
  .subscribe(x => console.log("[timer] result", x));
[timer] result 0
[timer] result 1
[timer] result 2
[timer] result 3
[timer] result 4
[timer] result 5
[timer] result 6
[timer] result 7
[timer] result 8
[timer] result 9

4 - Marble Diagrams

The Marble Diagrams is a timeline where you can illustrate the state of your observable during its execution.
The actors in this diagram are timeline and values(circle).
The timeline is used to represent the time during the execution of the observable though the circles indicate the values emitted.
But let me show you an example:
map marble diagram

This example is based on this code

import { Observable } from "rxjs";
import { map } from "rxjs/operators";

const source$ = new Observable<number>(observer => {
  let count = 0;
  const id = setInterval(() => {
    if (count++ < 3) {
      observer.next(count);
    } else {
      clearInterval(id);
      observer.complete();
    }
  }, 1000);
});

source$.pipe(map(value => value * 2)).subscribe({
  next: console.log,
});

As you can notice, in the diagram there are two timelines, one for the source and the other for the map operator.
In the first timeline you can see when the source emits the value, in the second timeline you can see the result of the transformation after the execution of the map operator.

To build a Marble diagram you need to keep in mind some easy rules: there is always a timeline that represents the source observable, there are N timelines as many operators as you need to display, every timeline illustrates the state of the values after the execution of the operator indicated in the timeline and finally, you need to use a circle to represent the values.

This tool is very convenient to illustrate the transformation of the observable during its execution and it helps us to have an image of the state of the observable execution.

In addition to the Marble Diagram you can use the Marble Testing to test the execution of you Observable.
The Marble testing uses a special format to represent the timeline and the value during the execution, but I will speak about it in the future.

To reinforce the Marble Diagram concept let me show you another example

import { Observable } from "rxjs";
import { delay, map } from "rxjs/operators";

const source$ = new Observable<number>(observer => {
  let count = 0;
  const id = setInterval(() => {
    if (count++ < 3) {
      observer.next(count);
    } else {
      clearInterval(id);
      observer.complete();
    }
  }, 1000);
});

source$
  .pipe(
    map(value => value * 2),
    delay(1500)
  )
  .subscribe({
    next: console.log,
  });

 

Marble Diagram Map+Delay

In this example you can see how the observable in the first operator doubles the value and then it waits 1.5 seconds before emitting the result.
To represent this case the marble diagram has 3 timelines, one with the source, one with the map operator and one with the delay operator. Every timeline indicates the value during the execution of its operator so you can see the behaviour of this implementation.

It's all from the marble diagram.


5 - Creation Operators

The Creation Operators are used to create new observables. They are divided into Creation Operators and Join Creation Operators.
The main difference between them consists in the fact that the Join Creation Operators create observable from other observables instead the Creation Operators create observables from objects which differ from observable.

Creation Operators

The Of operator is used to convert an argument to an observable.

import { of } from "rxjs";

of([1, 2, 3], 4, 5, 6).subscribe({
  next: res => console.log("[of]", res),
  complete: () => console.log("[of] complete"),
});
[of] [ 1, 2, 3 ]
[of] 4
[of] 5
[of] 6
[of] complete

of Marble Diagram
As you can see, this operator takes a list of arguments and converts them to values of the observable.

This operator creates an Observable from an Array, an array-like object, a Promise, an iterable object, or an Observable-like object.

from array

from([1, 2, 3]).subscribe(res => console.log("[from]", res));
[from] 1
[from] 2
[from] 3

from Marble Diagram
In this case we converted an array to an observable.
The from operator converts all the items in the array to values of the observable. It's common to use this operator when we need to convert the items of an array to the values to react our system.

from promise

from(
  new Promise(res => setTimeout(() => res("Hello from promise"), 3000))
).subscribe(res => console.log(new Date().toLocaleTimeString(), `[from]`, res));
13:47:52 [from] Hello from promise

In this case we converted a promise to an observable. It is common to convert a promise to an observable, or because we have multiple subscribers which need to react to this promise or because we need to use these promise inside of a pipe chain.

from iterator

const wait = async (time: number) => new Promise(res => setTimeout(res, time));
async function* hello() {
  yield "Hello";
  await wait(1000);
  yield "from";
  await wait(1000);
  yield "iterator";
}
from(hello()).subscribe({
  next: res => console.log(new Date().toLocaleTimeString(), `[from]`, res),
  complete: () =>
    console.log(new Date().toLocaleTimeString(), `[from] complete`),
});
13:48:42 [from] Hello
13:48:43 [from] from
13:48:44 [from] iterator
13:48:44 [from] complete

Here instead, the from operator converts an iterator to an observable. This is common when we need to react to every item of the iterator or when we need to use the generator inside of a pipe chain.

The fromEvent operator is usage to emit an observable in base of an event

Some examples: when a user clicks to a document element in HTML/JS or when a system emits an event in node.js eco system.
Here a practise example

import * as events from "events";
import { fromEvent } from "rxjs";

console.log(new Date().toLocaleTimeString(), "[fromEvent] start");

const em = new events.EventEmitter();
fromEvent(em, "custom-event").subscribe({
  next: res => console.log(new Date().toLocaleTimeString(), "[fromEvent]", res),
  complete: () =>
    console.log(new Date().toLocaleTimeString(), "[fromEvent] complete"),
});

setTimeout(() => {
  em.emit("custom-event", "Hello from event emitter");
}, 3000);
14:05:06 [fromEvent] start
14:05:09 [fromEvent] Hello from event emitter

fromEvent Marble Diagram

Creates an Observable that emits no items to the Observer and immediately emits a complete notification.

import { EMPTY } from "rxjs";

EMPTY.subscribe({
  next: res => console.log("[EMPTY]", res),
  complete: () => console.log("[EMPTY] complete"),
});
[EMPTY] complete

EMPTY Marble Diagram
This operator is used for scheduling the emission of the complete notification.

Creates an observable that will wait for a specified time period, or exact date, before emitting the number 0.

import { timer } from "rxjs";

console.log(new Date().toLocaleTimeString(), "[timer] start");
timer(1000).subscribe({
  next: res => console.log(new Date().toLocaleTimeString(), "[timer]", res),
  complete: () =>
    console.log(new Date().toLocaleTimeString(), "[timer] complete"),
});
14:14:34 [timer] start
14:14:35 [timer] 0
14:14:35 [timer] complete

timer Marble Diagram
This operator is similar to the setTimeout function in JS. It creates an observable and emits only a value after a time indicated as argument.

Creates an Observable that emits sequential numbers every specified interval of time, on a specified SchedulerLike.

import { interval } from "rxjs";

console.log(new Date().toLocaleTimeString(), "[interval] start");
interval(1000).subscribe({
  next: res => console.log(new Date().toLocaleTimeString(), "[interval]", res),
  complete: () =>
    console.log(new Date().toLocaleTimeString(), "[interval] complete"),
});
14:15:10 [interval] start
14:15:11 [interval] 0
14:15:12 [interval] 1
14:15:13 [interval] 2
14:15:14 [interval] 3
14:15:15 [interval] 4
...
...
...

interval Marble Diagram Text
This operator is similar to the setInterval function in JS. It creates an observable and emits a sequential number every specific time indicated as argument.
N.B. it start from ZERO and not from ONE

timer as special interval
Sometimes you need to create an interval with a specific period but you need to start it with a period different from the interval period. In these cases it is common to use the timer operator that accepts a period as second argument.
So you can indicate the time before emitting the first value as first argument and the period of the interval as second argument.

timer(1000, 1000).subscribe({
  next: res =>
    console.log(new Date().toLocaleTimeString(), "[timer as interval]", res),
  complete: () =>
    console.log(
      new Date().toLocaleTimeString(),
      "[timer as interval] complete"
    ),
});
14:25:56 [timer as interval] start
14:26:00 [timer as interval] 0
14:26:01 [timer as interval] 1
14:26:02 [timer as interval] 2
...
...

Join Creation Operators

Before starting, I clarify that all the examples use these three observable as inputs.

import { from, Observable } from "rxjs";

async function* hello() {
  const wait = async (time: number) =>
    new Promise(res => setTimeout(res, time));
  yield "Hello";
  await wait(1000);
  yield "from";
  await wait(500);
  yield "iterator";
}

export const iterator$ = from(hello());
export const arrayFrom$ = from(["Hello", "from", "array"]);
export const arrayOfWithDelay$ = new Observable<number>(subscriber => {
  let counter = 10;
  const id = setInterval(() => {
    if (counter > 0) {
      subscriber.next(counter--);
    } else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 500);
});

Combines multiple Observables to create an Observable whose values are calculated from the latest values of each of its input Observables.

import { combineLatest } from "rxjs";
import { arrayFrom$, arrayOfWithDelay$, iterator$ } from "../sources";

console.log(new Date().toLocaleTimeString(), `[combineLatest] start`);

combineLatest([iterator$, arrayFrom$, arrayOfWithDelay$]).subscribe({
  next: res =>
    console.log(new Date().toLocaleTimeString(), `[combineLatest]`, res),
  complete: () =>
    console.log(new Date().toLocaleTimeString(), `[combineLatest] complete`),
});
12:38:22 [combineLatest] start
12:38:22 [combineLatest] [ 'Hello', 'array', 10 ]
12:38:23 [combineLatest] [ 'from', 'array', 10 ]
12:38:23 [combineLatest] [ 'from', 'array', 9 ]
12:38:23 [combineLatest] [ 'iterator', 'array', 9 ]
12:38:23 [combineLatest] [ 'iterator', 'array', 8 ]
12:38:24 [combineLatest] [ 'iterator', 'array', 7 ]
12:38:24 [combineLatest] [ 'iterator', 'array', 6 ]
12:38:25 [combineLatest] [ 'iterator', 'array', 5 ]
12:38:25 [combineLatest] [ 'iterator', 'array', 4 ]
12:38:26 [combineLatest] [ 'iterator', 'array', 3 ]
12:38:26 [combineLatest] [ 'iterator', 'array', 2 ]
12:38:27 [combineLatest] [ 'iterator', 'array', 1 ]
12:38:27 [combineLatest] complete

combineLatest Marble Diagram
In this example, you can see how this operator emits an array of value every time an observable emits one value.
It's important to remember that the operator emits the first value when all the dependence observables emit the first value.
As you can see, the result of the combineLatest operator is an array where items respect the order of the observables in the declaration.

Accepts an Array of ObservableInput or a dictionary Object of ObservableInput and returns an Observable that emits either an array of values in the exact same order as the passed array, or a dictionary of values in the same shape as the passed dictionary.

import { forkJoin } from "rxjs";
import { arrayFrom$, arrayOfWithDelay$, iterator$ } from "../sources";

console.log(new Date().toLocaleTimeString(), `[forkJoin] start`);

forkJoin([iterator$, arrayFrom$, arrayOfWithDelay$]).subscribe({
  next: res => console.log(new Date().toLocaleTimeString(), `[forkJoin]`, res),
  complete: () =>
    console.log(new Date().toLocaleTimeString(), `[forkJoin] complete`),
});
14:38:58 [forkJoin] start
14:39:04 [forkJoin] [ 'iterator', 'array', 1 ]
14:39:04 [forkJoin] complete

forkJoin Marble Diagram
forkJoin is similar to the combineLatest operator, the difference is that the forkJoin operator emits only one value when all the observables are completed. In simple words, the forkJoin operator emits only the last value of the combineLatest operator.

Creates an output Observable which sequentially emits all values from the first given Observable and then moves on to the next.

import { concat } from "rxjs";
import { arrayFrom$, arrayOfWithDelay$, iterator$ } from "../sources";

console.log(new Date().toLocaleTimeString(), `[concat] start`);

concat(iterator$, arrayFrom$, arrayOfWithDelay$).subscribe({
  next: res => console.log(new Date().toLocaleTimeString(), `[concat]`, res),
  complete: () =>
    console.log(new Date().toLocaleTimeString(), `[concat] complete`),
});
14:44:23 [concat] start
14:44:23 [concat] Hello
14:44:24 [concat] from
14:44:24 [concat] iterator
14:44:24 [concat] Hello
14:44:24 [concat] from
14:44:24 [concat] array
14:44:25 [concat] 10
14:44:25 [concat] 9
14:44:26 [concat] 8
14:44:26 [concat] 7
14:44:27 [concat] 6
14:44:27 [concat] 5
14:44:28 [concat] 4
14:44:28 [concat] 3
14:44:29 [concat] 2
14:44:29 [concat] 1
14:44:30 [concat] complete

concat Marble Diagram
As you can see, this operator emits all the values of the observables in sequences.
Concat unlike of combineLatest does not run all observables in concurrency, but it runs the observables in sequence, it starts from the first and doesn't go to the next until the current is not completed.

Creates an output Observable which concurrently emits all values from every given input Observable.

import { merge } from "rxjs";
import { arrayFrom$, arrayOfWithDelay$, iterator$ } from "../sources";

console.log(new Date().toLocaleTimeString(), `[merge] start`);

merge(iterator$, arrayFrom$, arrayOfWithDelay$).subscribe({
  next: res => console.log(new Date().toLocaleTimeString(), `[merge]`, res),
  complete: () =>
    console.log(new Date().toLocaleTimeString(), `[merge] complete`),
});
14:58:48 [merge] start
14:58:48 [merge] Hello
14:58:48 [merge] from
14:58:48 [merge] array
14:58:48 [merge] Hello
14:58:48 [merge] 10
14:58:49 [merge] from
14:58:49 [merge] 9
14:58:49 [merge] iterator
14:58:49 [merge] 8
14:58:50 [merge] 7
14:58:50 [merge] 6
14:58:51 [merge] 5
14:58:51 [merge] 4
14:58:52 [merge] 3
14:58:52 [merge] 2
14:58:53 [merge] 1
14:58:53 [merge] complete

merge Marble Diagram
The merge operator is similar to the concat operator unlike that the merge operator runs all observables in concurrency mode, so, in this case, all the observables start together and every time an observable emits a value the merge operator emits this last value.

Returns an observable that mirrors the first source observable to emit an item.

import { race } from "rxjs";
import { arrayFrom$, arrayOfWithDelay$, iterator$ } from "../sources";

console.log(new Date().toLocaleTimeString(), `[race] start`);

race([iterator$, arrayFrom$, arrayOfWithDelay$]).subscribe({
  next: res => console.log(new Date().toLocaleTimeString(), `[race]`, res),
  complete: () =>
    console.log(new Date().toLocaleTimeString(), `[race] complete`),
});
15:09:03 [race] start
15:09:03 [race] Hello
15:09:03 [race] from
15:09:03 [race] array
15:09:03 [race] complete

race Marble Diagram
This operator is particular, it emits the first observable that emits the first value. In other words, it takes the faster observable and ignores the others.

Combines multiple Observables to create an Observable whose values are calculated from the values, in order, of each of its input Observables.

import { zip } from "rxjs";
import { arrayFrom$, arrayOfWithDelay$, iterator$ } from "../sources";

console.log(new Date().toLocaleTimeString(), `[zip] start`);

zip([iterator$, arrayFrom$, arrayOfWithDelay$]).subscribe({
  next: res => console.log(new Date().toLocaleTimeString(), `[zip]`, res),
  complete: () =>
    console.log(new Date().toLocaleTimeString(), `[zip] complete`),
});
15:09:27 [zip] start
15:09:27 [zip] [ 'Hello', 'Hello', 10 ]
15:09:28 [zip] [ 'from', 'from', 9 ]
15:09:28 [zip] [ 'iterator', 'array', 8 ]
15:09:28 [zip] complete

zip Marble Diagram
This operator may seem strange, but it can use to combine in order the values of difference observables.
In this example we have 3 observables:

  • iterator$: ['Hello', 'from', 'iterator', '!']
  • arrayFrom$: ['Hello', 'from', 'array', '!']
  • arrayOfWithDelay$: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

With the zip operator we combine the values in order of them index:

  • ['Hello', 'Hello', 10]
  • ['from', 'from', 9]
  • ['iterator', 'array', 8]

As you can see, the operator stops emitting values at the index of the first completed observable.


6 - Filtering Operators

As you can imagine these operators are used to filter our sources.
Well, let's get started.

Ignores source values for duration milliseconds, then emits the most recent value from the source Observable, then repeats this process.

import { Observable } from "rxjs";
import { auditTime } from "rxjs/operators";

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if (count < 10) {
      subscriber.next(++count);
    } else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 1000);
});

const result = input$.pipe(auditTime(2000));

result.subscribe({
  next: x =>
    console.log(
      `${new Date().toLocaleTimeString()} - [auditTime result]: ${x}`
    ),
});
11:15:25 - [auditTime result]: 2
11:15:27 - [auditTime result]: 4
11:15:29 - [auditTime result]: 6
11:15:31 - [auditTime result]: 8
11:15:33 - [auditTime result]: 10

auditTime Marble Diagram
This operator is particular: for the indicated time it ignores the emitted values, and when the time is passed, it emits the last value emitted from the source. It can be used when we have a source that emits a lot of values but we need to get only the value in a specific interval, for example, we have a temperature sensor that emits the temperature every second but we need to take the value every minute, using auditTime we can get the last value of every minute.

Emits a notification from the source Observable only after a particular time span has passed without another source emission.

import { Observable } from "rxjs";
import { debounceTime, tap } from "rxjs/operators";

const timesInSecond = [1, 0.5, 3, 1.5, 3, 1];

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  (function next() {
    const seconds = timesInSecond[count++];
    setTimeout(() => {
      subscriber.next(seconds);

      if (count > 5) {
        subscriber.complete();
        return;
      }

      next();
    }, seconds * 1000);
  })();
});

const result = input$.pipe(
  tap(x =>
    console.log(
      `${new Date().toLocaleTimeString()} - [before debounceTime]: ${x}`
    )
  ),
  debounceTime(2000)
);
result.subscribe({
  next: x =>
    console.log(
      `${new Date().toLocaleTimeString()} - [debounceTime result]: ${x}`
    ),
});
09:44:29 - [before debounceTime]: 1
09:44:29 - [before debounceTime]: 0.5
09:44:31 - [debounceTime result]: 0.5
09:44:32 - [before debounceTime]: 3
09:44:34 - [before debounceTime]: 1.5
09:44:36 - [debounceTime result]: 1.5
09:44:37 - [before debounceTime]: 3
09:44:38 - [before debounceTime]: 1
09:44:38 - [debounceTime result]: 1

debounceTime Marble Diagram
After receiving a value, this operator waits for the indicated time before emitting the value. If during this time a new value is emitted, the operator ignores the previous value and waits again, otherwise, if nothing happens it emits the value.
For example, this operator is used to manage the autocomplete text boxes that search items from an API; with this operator, we can prevent calling the API on every click on the keyboard but we can wait until the user finish writing some letters before executing the search.

Emits a value from the source Observable, then ignores subsequent source values for duration milliseconds, then repeats this process.

import { Observable } from "rxjs";
import { tap, throttleTime } from "rxjs/operators";

const timesInSecond = [1, 0.5, 3, 1.5, 3, 1];

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  (function next() {
    const seconds = timesInSecond[count++];
    setTimeout(() => {
      subscriber.next(seconds);

      if (count > 5) {
        subscriber.complete();
        return;
      }

      next();
    }, seconds * 1000);
  })();
});


const result = input$.pipe(
  tap(x =>
    console.log(
      `${new Date().toLocaleTimeString()} - [before throttleTime]: ${x}`
    )
  ),
  throttleTime(2000)
);
result.subscribe({
  next: x =>
    console.log(
      `${new Date().toLocaleTimeString()} - [throttleTime result]: ${x}`
    ),
});
10:41:46 - [before throttleTime]: 1
10:41:46 - [throttleTime result]: 1
10:41:46 - [before throttleTime]: 0.5
10:41:49 - [before throttleTime]: 3
10:41:49 - [throttleTime result]: 3
10:41:51 - [before throttleTime]: 1.5
10:41:54 - [before throttleTime]: 3
10:41:54 - [throttleTime result]: 3
10:41:55 - [before throttleTime]: 1

throttleTime Marble Diagram
After receiving a value, this operator emits it and then waits for the indicated time. If during that time the source emits other values this operator ignores them, it repeats this process for life.
This operator is commonly used to prevent multiple clicks in some buttons or to prevent many executions of an action in a small period.

Returns an Observable that emits all items emitted by the source Observable that are distinct by comparison from previous items.

import { Observable } from "rxjs";
import { distinct } from "rxjs/operators";

const input$ = new Observable<number | string>(subscriber => {
  let count = 0;
  const array = [1, 1, "1", 2, 2, 3, 3];
  const id = setInterval(() => {
    if (count < array.length) {
      subscriber.next(array[count++]);
    } else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 1000);
});

input$.pipe(distinct()).subscribe({
  next: x =>
    console.log(`${new Date().toLocaleTimeString()} - [distinct]: ${x}`),
});
12:22:58 - [distinct]: 1
12:23:00 - [distinct]: 1
12:23:01 - [distinct]: 2
12:23:03 - [distinct]: 3

distinct Marble Diagram
This operator emits the value only if the value is different from the last value emitted. It's important to remember that the distinct operator checks the equality using the strict equal operator (===). It's also possible, if your source emits an object, to indicate the property used to check the equality distinct(p => p.id).

import { Observable } from "rxjs";
import { distinctUntilChanged } from "rxjs/operators";

type State = { id: number; value: string };

const createValue = (id: number): State => ({ id, value: String(id) });
const array = [
  createValue(1),
  createValue(1),
  createValue(1),
  createValue(2),
  createValue(2),
  createValue(2),
  createValue(3),
  createValue(3),
  createValue(3),
  createValue(4),
  createValue(4),
  createValue(4),
];

const input$ = new Observable<State>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if (count < array.length) {
      subscriber.next(array[count++]);
    } else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 1000);
});

input$
  .pipe(distinctUntilChanged((prev, curr) => prev.id === curr.id))
  .subscribe({
    next: x =>
      console.log(
        `${new Date().toLocaleTimeString()} - [distinctUntilChanged]`,
        x
      ),
  });
11:45:39 - [distinctUntilChanged] { id: 1, value: '1' }
11:45:42 - [distinctUntilChanged] { id: 2, value: '2' }
11:45:45 - [distinctUntilChanged] { id: 3, value: '3' }
11:45:48 - [distinctUntilChanged] { id: 4, value: '4' }

distinctUntilChanged Marble Diagram
This operator is similar to the distinct operator but you can indicate a function that accepts the previous and the current item as arguments and indicates the algorithm to determinate if the two items are equals.

Returns an Observable that emits all items emitted by the source Observable that are distinct by comparison from the previous item, using a property accessed by using the key provided to check if the two items are distinct.

import { Observable } from "rxjs";
import { distinctUntilKeyChanged } from "rxjs/operators";

type State = { id: number; value: string };

const createValue = (id: number): State => ({ id, value: String(id) });
const array = [
  createValue(1),
  createValue(1),
  createValue(1),
  createValue(2),
  createValue(2),
  createValue(2),
  createValue(3),
  createValue(3),
  createValue(3),
  createValue(4),
  createValue(4),
  createValue(4),
];

const input$ = new Observable<State>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if (count < array.length) {
      subscriber.next(array[count++]);
    } else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 1000);
});

input$.pipe(distinctUntilKeyChanged("id")).subscribe({
  next: x =>
    console.log(
      `${new Date().toLocaleTimeString()} - [distinctUntilKeyChanged]`,
      x
    ),
});
11:46:12 - [distinctUntilKeyChanged] { id: 1, value: '1' }
11:46:15 - [distinctUntilKeyChanged] { id: 2, value: '2' }
11:46:18 - [distinctUntilKeyChanged] { id: 3, value: '3' }
11:46:21 - [distinctUntilKeyChanged] { id: 4, value: '4' }

distinctUntilKeyChanged Marble Diagram
This operator is similar to the distinct operator but you can indicate the name of the property used to compare the equality.

Filter items emitted by the source Observable by only emitting those that satisfy a specified predicate

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

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if (count < 10) {
      subscriber.next(++count);
    } else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 1000);
});

input$.pipe(filter(x => x % 2 === 0)).subscribe({
  next: x => console.log(`${new Date().toLocaleTimeString()} - [filter]: ${x}`),
});

 

11:46:43 - [filter]: 2
11:46:45 - [filter]: 4
11:46:47 - [filter]: 6
11:46:49 - [filter]: 8
11:46:51 - [filter]: 10

filter Marble Diagram
As you can imagine, this operator accepts a function that has an argument (the current item) and returns a boolean that indicates if the value can be emitted or must be ignored.

Emits only the first value (or the first value that meets some condition) emitted by the source Observable.

import { Observable } from "rxjs";
import { first } from "rxjs/operators";

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if (count < 10) {
      subscriber.next(++count);
    } else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 1000);
});

console.log(`${new Date().toLocaleTimeString()} - [first] start`)
input$.pipe(first()).subscribe({
  next: x => console.log(`${new Date().toLocaleTimeString()} - [first]: ${x}`),
  complete: () => console.log(`${new Date().toLocaleTimeString()} - [first] complete`),
});

 

09:47:15 - [first] start
09:47:16 - [first]: 1
09:47:16 - [first] complete

first Marble Diagram
This operator takes the first value and ignores the others.
It's important to remember that when the first operator emits the value it completes the subscription too.

Returns an Observable that emits only the last item emitted by the source Observable. It optionally takes a predicate function as a parameter, in which case, rather than emitting the last item from the source Observable, the resulting Observable will emit the last item from the source Observable that satisfies the predicate.

import { Observable } from "rxjs";
import { last } from "rxjs/operators";

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if (count < 10) {
      subscriber.next(++count);
    } else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 1000);
});

console.log(`${new Date().toLocaleTimeString()} - [last] start`)
input$.pipe(last()).subscribe({
  next: x => console.log(`${new Date().toLocaleTimeString()} - [last]: ${x}`),
  complete: () => console.log(`${new Date().toLocaleTimeString()} - [last] complete`),
});
09:48:14 - [last] start
09:48:25 - [last]: 10
09:48:25 - [last] complete

last Marble Diagram
This operator takes the last value and ignores the others.

Returns an Observable that skips the first count items emitted by the source Observable.

import { Observable } from "rxjs";
import { skip } from "rxjs/operators";

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if(count < 5) subscriber.next(count++);
    else {
      clearInterval(id);
      subscriber.complete();
    } 
  }, 1000);
  return () => {
    clearInterval(id);
    subscriber.complete();
  };
});

input$.pipe(skip(2)).subscribe({
  next: x => console.log(`${new Date().toLocaleTimeString()} - [skip]: ${x}`),
  complete: () =>
    console.log(`${new Date().toLocaleTimeString()} - [skip]: complete`),
});
10:33:41 - [skip]: 2
10:33:42 - [skip]: 3
10:33:43 - [skip]: 4
10:33:44 - [skip]: complete

skip Marble Diagram
As you can see, this operator skips the first x items we indicate, if we write skip(1) the operator skips the first value, if we write skip(2) the operator skips the first two values and so on.

Skip a specified number of values before the completion of an observable.

import { Observable } from "rxjs";
import { skipLast } from "rxjs/operators";

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if (count < 5) subscriber.next(count++);
    else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 1000);
});

console.log(`${new Date().toLocaleTimeString()} - [skipLast]: start`)

input$.pipe(skipLast(3)).subscribe({
  next: x => console.log(`${new Date().toLocaleTimeString()} - [skipLast]: ${x}`),
  complete: () =>
    console.log(`${new Date().toLocaleTimeString()} - [skipLast]: complete`),
});
10:33:58 - [skipLast]: start
10:34:02 - [skipLast]: 0
10:34:03 - [skipLast]: 1
10:34:04 - [skipLast]: complete

skipLast Marble Diagram
This operator skips the last x items we indicate, if we write skipLast(1) the operator skips the last value, if we write skipLast(2) the operator skips the last two values and so on.

Returns an Observable that skips items emitted by the source Observable until a second Observable emits an item.

import { Observable } from "rxjs";
import { skipUntil } from "rxjs/operators";

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if (count < 5) subscriber.next(count++)
    else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 1000);
});

const untilInput$ = new Observable<void>(subscriber => {
  setTimeout(() => {
    console.log(
      `${new Date().toLocaleTimeString()} - untilInput$ emit`
    );
    subscriber.next();
    subscriber.complete();
  }, 5000);
});

input$.pipe(skipUntil(untilInput$)).subscribe({
  next: x =>
    console.log(`${new Date().toLocaleTimeString()} - [skipUntil]: ${x}`),
  complete: () =>
    console.log(`${new Date().toLocaleTimeString()} - [skipUntil]: complete`),
});
10:34:37 - untilInput$ emit
10:34:37 - [skipUntil]: 4
10:34:38 - [skipUntil]: complete

skipUntil Marble Diagram
This operator skips all the values until another observable doesn't emit a value.

Returns an Observable that skips all items emitted by the source Observable as long as a specified condition holds true, but emits all further source items as soon as the condition becomes false.

import { Observable } from "rxjs";
import { skipWhile } from "rxjs/operators";

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if (count < 5) subscriber.next(count++);
    else {
      subscriber.next(count++);
      subscriber.complete();
    }
  }, 1000);
  return () => {
    clearInterval(id);
  };
});

input$.pipe(skipWhile(val => val < 3)).subscribe({
  next: x =>
    console.log(`${new Date().toLocaleTimeString()} - [skipWhile]: ${x}`),
  complete: () =>
    console.log(`${new Date().toLocaleTimeString()} - [skipWhile]: complete`),
});
10:36:34 - [skipWhile]: 3
10:36:35 - [skipWhile]: 4
10:36:36 - [skipWhile]: 5
10:36:36 - [skipWhile]: complete

skipWhile Marble Diagram
This operator skips all the values 鈥嬧媢ntil the predicate isn't true, after the first true result, the skipWhile operator doesn't eval anymore the predicate and emits all the values.

Emits only the first count values emitted by the source Observable.

import { Observable } from "rxjs";
import { take } from "rxjs/operators";

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    subscriber.next(count++);
  }, 1000);
  return () => {
    clearInterval(id);
    subscriber.complete();
  };
});

input$.pipe(take(2)).subscribe({
  next: x => console.log(`${new Date().toLocaleTimeString()} - [take]: ${x}`),
  complete: () =>
    console.log(`${new Date().toLocaleTimeString()} - [take]: complete`),
});
10:39:39 - [take]: 0
10:39:40 - [take]: 1
10:39:40 - [take]: complete

take Marble Diagram
The take operator is the opposite of the skip operator, if the skip ignores x values, the take operator emits x values.
So if you write take(1) you get only the first value, if you write take(2) you get only the first two values.
It's important to remember that when the operator emits the last value completes the subscription too.

Waits for the source to complete, then emits the last N values from the source, as specified by the count argument.

import { Observable } from "rxjs";
import { takeLast } from "rxjs/operators";

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if (count < 5) subscriber.next(count++);
    else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 1000);
});

console.log(`${new Date().toLocaleTimeString()} - [takeLast]: start`)

input$.pipe(takeLast(3)).subscribe({
  next: x => console.log(`${new Date().toLocaleTimeString()} - [takeLast]: ${x}`),
  complete: () =>
    console.log(`${new Date().toLocaleTimeString()} - [takeLast]: complete`),
});
10:40:08 - [takeLast]: start
10:40:14 - [takeLast]: 2
10:40:14 - [takeLast]: 3
10:40:14 - [takeLast]: 4
10:40:14 - [takeLast]: complete

takeLast Marble Diagram
This operator takes the last x items we indicate, if we write takeLast(1) the operator takes the last value, if we write takeLast(3) the operator takes the last three values, and so on.

Emits the values emitted by the source Observable until a notifier Observable emits a value.

import { Observable } from "rxjs";
import { takeUntil } from "rxjs/operators";

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    if (count < 5) subscriber.next(++count);
    else {
      clearInterval(id);
      subscriber.complete();
    }
  }, 1000);
});

const untilInput$ = new Observable<void>(subscriber => {
  setTimeout(() => {
    console.log(
      `${new Date().toLocaleTimeString()} - untilInput$ emit`
    );
    subscriber.next();
    subscriber.complete();
  }, 2500);
});

input$.pipe(takeUntil(untilInput$)).subscribe({
  next: x =>
    console.log(`${new Date().toLocaleTimeString()} - [takeUntil]: ${x}`),
  complete: () =>
    console.log(`${new Date().toLocaleTimeString()} - [takeUntil]: complete`),
});
10:40:38 - [takeUntil]: 1
10:40:39 - [takeUntil]: 2
10:40:42 - untilInput$ emit
10:40:42 - [takeUntil]: complete

takeUntil Marble Diagram
This operator takes all the values until another observable doesn't emit a value, after that it complete the subscription.

Emits values emitted by the source Observable so long as each value satisfies the given predicate, and then completes as soon as this predicate is not satisfied.

import { Observable } from "rxjs";
import { takeWhile } from "rxjs/operators";

const input$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    subscriber.next(count++);
  }, 1000);
  return () => {
    clearInterval(id);
    subscriber.complete();
  };
});

input$.pipe(takeWhile(val => val < 3)).subscribe({
  next: x =>
    console.log(`${new Date().toLocaleTimeString()} - [takeWhile]: ${x}`),
  complete: () =>
    console.log(`${new Date().toLocaleTimeString()} - [takeWhile]: complete`),
});
10:41:10 - [takeWhile]: 0
10:41:11 - [takeWhile]: 1
10:41:12 - [takeWhile]: 2
10:41:13 - [takeWhile]: complete

takeWhile Marble Diagram
This operator emits all the values 鈥嬧媢ntil the predicate is true, after that the takeWhile operator completes the subscription.


7 - Transformation Operators

These operators are used to modify the value received.
But cut the chatter, and let's start.

Applies a given project function to each value emitted by the source Observable, and emits the resulting values as an Observable.

import { interval } from 'rxjs';
import { map, take, tap } from 'rxjs/operators';

const source$ = interval(1000, ).pipe(
    take(5),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Generated`, val);
    }),
)

source$.pipe(
    map(value => Math.pow(value, 2))
)
.subscribe({
    next: value => console.log(`${new Date().toLocaleTimeString()}: map`, value),
    complete: () => console.log(`${new Date().toLocaleTimeString()}: map: complete`),
})
16:58:51: Generated 0
16:58:51: map 0
16:58:52: Generated 1
16:58:52: map 1
16:58:53: Generated 2
16:58:53: map 4
16:58:54: Generated 3
16:58:54: map 9
16:58:55: Generated 4
16:58:55: map 16
16:58:55: map: complete

The map operator is like the map function of the arrays.
It transforms the value using the function passed as an argument.

Emits the given constant value on the output Observable every time the source Observable emits a value.

import { interval } from 'rxjs';
import { mapTo, take, tap } from 'rxjs/operators';

const source1$ = interval(1000).pipe(
    take(5),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Source1 Generated`, val);
    }),
)

console.log(`${new Date().toLocaleTimeString()}: mapTo: start`)
source1$.pipe(
    mapTo(1000)
)
.subscribe({
    next: value => console.log(`${new Date().toLocaleTimeString()}: mapTo`, value),
    complete: () => console.log(`${new Date().toLocaleTimeString()}: mapTo: complete`),
})
17:03:01: mapTo: start
17:03:02: Source1 Generated 0
17:03:02: mapTo 1000
17:03:03: Source1 Generated 1
17:03:03: mapTo 1000
17:03:04: Source1 Generated 2
17:03:04: mapTo 1000
17:03:05: Source1 Generated 3
17:03:05: mapTo 1000
17:03:06: Source1 Generated 4
17:03:06: mapTo 1000
17:03:06: mapTo: complete

The operator is similar to the map operator, but it returns a fixed value that does not depend on the source.

Now I start to talk about the operators: concat, exhaust, merge and switch.
These operators are similar to the others but they have some minimum differences that change their behaviour, and if you choose the wrong implementations you may not have the aspected result.

Projects each source value to an Observable which is merged in the output Observable, in a serialized fashion waiting for each one to complete before merging the next.

import { interval, Observable, of } from 'rxjs';
import { concatMap, delay, take, tap } from 'rxjs/operators';

const powWithDelay$ = (value: number): Observable<number> => of(Math.pow(value, 2)).pipe(delay(2000))

const source$ = interval(1000).pipe(
    take(5),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Generated`, val);
    }),
)

source$.pipe(
    concatMap(value => powWithDelay$(value))
)
.subscribe({
    next: value => console.log(`${new Date().toLocaleTimeString()}: concatMap`, value),
    complete: () => console.log(`${new Date().toLocaleTimeString()}: concatMap: complete`),
})
17:54:07: Generated 0
17:54:08: Generated 1
17:54:09: Generated 2
17:54:09: concatMap 0
17:54:10: Generated 3
17:54:11: Generated 4
17:54:11: concatMap 1
17:54:13: concatMap 4
17:54:15: concatMap 9
17:54:17: concatMap 16
17:54:17: concatMap: complete

This operator is used to concatenate different observables.
The first source starts to emit the values, and the concatMap operator runs another observable for every value. The result values of the last observables are emitted as results of the operator concat.
You should remember that the concatMap operator runs only one value at a time. You can notice from the previous examples that the values 0,1 and 2 are emitted before the result of the first concatMap (0), and after that, you can notice the emission of the values 3 and 4 before the result of the second emitted value. The particular behaviour of the concatMap operator is better visible after the last generated value from the first source (4) because we can see all the concatMap results emitted every 2 seconds from each other.

Here's the marble diagram to explain better the behaviour:
ConcatMap Marble Diagram

Projects each source value to the same Observable which is merged multiple times in a serialized fashion on the output Observable.

import { interval } from 'rxjs';
import { concatMapTo, skip, take, tap } from 'rxjs/operators';

const source1$ = interval(1000, ).pipe(
    skip(2),
    take(2),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Source1 Generated`, val);
    }),
)

const source2$ = interval(2000, ).pipe(
    take(2),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Source2 Generated`, val);
    }),
)

console.log(`${new Date().toLocaleTimeString()}: concatMapTo: start`)
source1$.pipe(
    concatMapTo(source2$)
)
.subscribe({
    next: value => console.log(`${new Date().toLocaleTimeString()}: concatMapTo`, value),
    complete: () => console.log(`${new Date().toLocaleTimeString()}: concatMapTo: complete`),
})
18:12:28: concatMapTo: start
18:12:31: Source1 Generated 2
18:12:32: Source1 Generated 3
18:12:33: Source2 Generated 0
18:12:33: concatMapTo 0
18:12:35: Source2 Generated 1
18:12:35: concatMapTo 1
18:12:37: Source2 Generated 0
18:12:37: concatMapTo 0
18:12:39: Source2 Generated 1
18:12:39: concatMapTo 1
18:12:39: concatMapTo: complete

This operator is like the concatMap apart from the fact that it returns another observable that does not depend on the value received.
It can be used when an observable emit a value and we need to run another observable.
For example, we have a source that is a timer, and on every tick, we need to call an API. If we use the concatMapTo we can implement this solution easier.

Projects each source value to an Observable which is merged in the output Observable only if the previous projected Observable has completed.

import { interval, Observable, of } from 'rxjs';
import { delay, exhaustMap, take, tap } from 'rxjs/operators';

const powWithDelay$ = (value: number): Observable<number> => of(Math.pow(value, 2)).pipe(delay(2000))

const source$ = interval(1000, ).pipe(
    take(5),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Generated`, val);
    }),
)

source$.pipe(
    exhaustMap(value => powWithDelay$(value))
)
.subscribe({
    next: value => console.log(`${new Date().toLocaleTimeString()}: exhaustMap`, value),
    complete: () => console.log(`${new Date().toLocaleTimeString()}: exhaustMap: complete`),
})
18:17:47: Generated 0
18:17:48: Generated 1
18:17:49: Generated 2
18:17:49: exhaustMap 0
18:17:50: Generated 3
18:17:51: Generated 4
18:17:52: exhaustMap 9
18:17:52: exhaustMap: complete

The exhaustMap operator syntactically is equal to the concat operator, but it has a different behavior: this operator during the execution of a projection ignores all other values received until the execution is not ended. If you see the result in the console of the before example, you can notice that the power of the values 1, 2, and 4 has never been shown.

The marble diagram of the exhaustMap operator in the previous example:
ExhaustMap Marble Diagram

Projects each source value to an Observable which is merged in the output Observable.

import { interval, Observable, of } from 'rxjs';
import { delay, mergeMap, take, tap } from 'rxjs/operators';

const powWithDelay$ = (value: number): Observable<number> => of(Math.pow(value, 2)).pipe(delay(2000))

const source$ = interval(1000, ).pipe(
    take(5),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Generated`, val);
    }),
)

source$.pipe(
    mergeMap(value => powWithDelay$(value))
)
.subscribe({
    next: value => console.log(`${new Date().toLocaleTimeString()}: mergeMap`, value),
    complete: () => console.log(`${new Date().toLocaleTimeString()}: mergeMap: complete`),
})
18:18:16: Generated 0
18:18:17: Generated 1
18:18:18: Generated 2
18:18:18: mergeMap 0
18:18:19: mergeMap 1
18:18:19: Generated 3
18:18:20: mergeMap 4
18:18:20: Generated 4
18:18:21: mergeMap 9
18:18:22: mergeMap 16
18:18:22: mergeMap: complete

The mergeMap operator is syntactically like concat and exhaust but it executes the projection as soon as a value arrives. This behaviour can open some strange cases, for example, it's not guaranteed the order of the result values with the order of the source values.
Let me give an example: we have a source that emits some values every 500ms, in the mergeMap operator we call an http API; the first value calls the API and the response arrives after 1.5seconds, the second value calls the same api but the response arrives after 200ms. The mergeMap operator in this case emits the result of the second value primarily and the result of the first one secondly.
As you can imagine, in some cases this behaviour can be wrong or unexpected, so, if you need to use the mergeMap operator, remember what I said before and ask yourself if it can produce some side effects or if it is the right solution of your problems.

Here is the MergeMap marble Diagram of the previous example:
MergeMap Marble Diagram

Projects each source value to the same Observable which is merged multiple times in the output Observable.

import { interval } from 'rxjs';
import { mergeMapTo, skip, take, tap } from 'rxjs/operators';

const source1$ = interval(1000, ).pipe(
    skip(2),
    take(2),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Source1 Generated`, val);
    }),
)

const source2$ = interval(2000, ).pipe(
    take(2),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Source2 Generated`, val);
    }),
)

console.log(`${new Date().toLocaleTimeString()}: mergeMap: start`)
source1$.pipe(
    mergeMapTo(source2$)
)
.subscribe({
    next: value => console.log(`${new Date().toLocaleTimeString()}: mergeMap`, value),
    complete: () => console.log(`${new Date().toLocaleTimeString()}: mergeMap: complete`),
})
18:18:53: mergeMap: start
18:18:56: Source1 Generated 2
18:18:57: Source1 Generated 3
18:18:58: Source2 Generated 0
18:18:58: mergeMap 0
18:18:59: Source2 Generated 0
18:18:59: mergeMap 0
18:19:00: Source2 Generated 1
18:19:00: mergeMap 1
18:19:01: Source2 Generated 1
18:19:01: mergeMap 1
18:19:01: mergeMap: complete

This operator is like the mergeMap apart from the fact that it returns another observable that does not depend on the value received.
It can be used when an observable emits a value and we need to run another observable.
In simple words, it's like the concatMapTo with the behaviour of the mergeMap operator behind the scene.

Projects each source value to an Observable which is merged in the output Observable, emitting values only from the most recently projected Observable.

import { interval, Observable, of } from 'rxjs';
import { delay, switchMap, take, tap } from 'rxjs/operators';

const powWithDelay$ = (value: number): Observable<number> => of(Math.pow(value, 2)).pipe(delay(2000))

const source$ = interval(1000, ).pipe(
    take(5),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Generated`, val);
    }),
)

source$.pipe(
    switchMap(value => powWithDelay$(value))
)
.subscribe({
    next: value => console.log(`${new Date().toLocaleTimeString()}: switchMap`, value),
    complete: () => console.log(`${new Date().toLocaleTimeString()}: switchMap: complete`),
})
18:19:16: Generated 0
18:19:17: Generated 1
18:19:18: Generated 2
18:19:19: Generated 3
18:19:20: Generated 4
18:19:22: switchMap 16
18:19:22: switchMap: complete

The switchMap operator is syntactically like concat, exhaust and merge. It executes the projection as soon as a value arrives, but when a new value arrives, if the projection of the previous value is in execution, it kills it and starts the execution of the projection for the new value.
It can be used for example for searching the data of an autocomplete input. When the user types a new letter and emits a new value, with this operator we can stop the previous search if it's on execution and start the new one.

Here is the SwitchMap marble Diagram of the previous example:
SwitchMap Marble Diagram

Projects each source value to the same Observable which is flattened multiple times with switchMap in the output Observable.

import { interval } from 'rxjs';
import { skip, switchMapTo, take, tap } from 'rxjs/operators';

const source1$ = interval(1000, ).pipe(
    skip(2),
    take(2),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Source1 Generated`, val);
    }),
)

const source2$ = interval(2000, ).pipe(
    take(2),
    tap(val => {
        console.log(`${new Date().toLocaleTimeString()}: Source2 Generated`, val);
    }),
)

console.log(`${new Date().toLocaleTimeString()}: switchMap: start`)
source1$.pipe(
    switchMapTo(source2$)
)
.subscribe({
    next: value => console.log(`${new Date().toLocaleTimeString()}: switchMap`, value),
    complete: () => console.log(`${new Date().toLocaleTimeString()}: switchMap: complete`),
})
18:19:38: switchMap: start
18:19:41: Source1 Generated 2
18:19:42: Source1 Generated 3
18:19:44: Source2 Generated 0
18:19:44: switchMap 0
18:19:46: Source2 Generated 1
18:19:46: switchMap 1
18:19:46: switchMap: complete

This operator is like the switchMap apart from the fact that it returns another observable that does not depend on the value received.
It can be used when an observable emits a value and we need to run another observable.
In simple words, it's like the concatMapTo or the mergeMapTo with the behaviour of the switchMap operator behind the scene.

with this operator, we have finish the explanation of the 4 "special" operators (concat, exhaust, merge and switch). As you can see they are similar to each other but if you choose the wrong one it can produces some unexpected side effects.
I hope these examples can help you in future to choose the right operator for you

Groups pairs of consecutive emissions together and emits them as an array of two values.

import { of } from 'rxjs';
import { pairwise } from 'rxjs/operators';

const source$ = of(1, 2, 3, 4, 5);

source$.pipe(
    pairwise()
)
.subscribe({
    next: value => console.log(`${new Date().toLocaleTimeString()}: pairwise`, value),
})
18:20:02: pairwise [ 1, 2 ]
18:20:02: pairwise [ 2, 3 ]
18:20:02: pairwise [ 3, 4 ]
18:20:02: pairwise [ 4, 5 ]

This operator is used to get as result a tuple where in the first index there is the previous value and in the second index there is the current value. As you can imagine, if your source emits only one value the pairwise operator will never emit a value.

Useful for encapsulating and managing state. Applies an accumulator (or "reducer function") to each value from the source after an initial state is established -- either via a seed value (second argument), or from the first value from the source.

import { of } from 'rxjs';
import { scan } from 'rxjs/operators';

const source$ = of(1, 2, 3, 4, 5);

source$.pipe(
    scan((acc, curr) => acc + curr, 0)
)
.subscribe({
    next: value => console.log(`${new Date().toLocaleTimeString()}: scan`, value),
})
18:20:26: scan 1
18:20:26: scan 3
18:20:26: scan 6
18:20:26: scan 10
18:20:26: scan 15

This operator is similar to the reduce method of the array. We can create an accumulator and every time a new value is emitted from the source we can update this accumulator and return it as result.

Ok, that's all from the Transformation Operators, I hope this examples can help you in future and I hope you have achieved a clear idea of how and when to use these operators.


8 - Join Operators

I'll speak about the Join Operators. Some of these operators are similar to other operators shown in the Creation Operators but they are used in different contexts, ok let's start!

Flattens an Observable-of-Observables by applying combineLatest when the Observable-of-Observables completes.

import { interval, timer } from 'rxjs';
import { combineLatestAll, map, take } from 'rxjs/operators';

const A_CHAR_CODE = 65
const createSource = (index: number) =>
    interval((index + 1) * 1000).pipe(
        take(3),
        map(value => `${String.fromCharCode(A_CHAR_CODE + index)}${String.fromCharCode(A_CHAR_CODE + value)}`)
    )

console.log(`${new Date().toLocaleTimeString()}: combineLatestAll start`)
const source$ = timer(0, 1000).pipe(
    take(3)
);
source$.pipe(
    map(createSource),
    combineLatestAll()
).subscribe({
    next: val => console.log(`${new Date().toLocaleTimeString()}: combineLatestAll`, val)
});
16:22:07: combineLatestAll start
16:22:12: combineLatestAll [ 'AB', 'BA', 'CA' ]
16:22:12: combineLatestAll [ 'AC', 'BA', 'CA' ]
16:22:13: combineLatestAll [ 'AC', 'BB', 'CA' ]
16:22:15: combineLatestAll [ 'AC', 'BB', 'CB' ]
16:22:15: combineLatestAll [ 'AC', 'BC', 'CB' ]
16:22:18: combineLatestAll [ 'AC', 'BC', 'CC' ]

CombineLatestAll Marble Diagram
As you can see, this operator is the brother of the combineLatest operator discussed in a previous article RxJS - Creation Operators.
Its job is to convert an Observable of Observables to an array of values, where each item of this array is a value of an observable.
When we use this operator we need to remember that the first value emitted by the operator is when all the observables have emitted at least one value.
This operator is used when in the pipe chain we have an operator that emits a list of observables; using this operator we can flat the values in a single array including all the values emitted by these observables.

Converts a higher-order Observable into a first-order Observable by concatenating the inner Observables in order.

import { interval, timer } from 'rxjs';
import { concatAll, map, take } from 'rxjs/operators';

const createSource = (index: number) => interval(index * 1000).pipe(
        take(3),
        map(value => `${index}-${value + 1}`)
    )

console.log(`${new Date().toLocaleTimeString()}: concatAll start`)
const source$ = timer(0, 1000).pipe(
    take(3)
);
source$.pipe(
    map(v => createSource(v + 1)),
    concatAll()
).subscribe({
    next: val => console.log(`${new Date().toLocaleTimeString()}: concatAll`, val)
});
15:38:29: concatAll start
15:38:30: concatAll 1-1
15:38:31: concatAll 1-2
15:38:32: concatAll 1-3
15:38:34: concatAll 2-1
15:38:36: concatAll 2-2
15:38:38: concatAll 2-3
15:38:41: concatAll 3-1
15:38:44: concatAll 3-2
15:38:47: concatAll 3-3

ConcatAll Marble Diagram
The concatAll operator concatenates an Observable of Observables and emits all the observables in sequence.
It's important to remember that when we use this operator, the observables are running one after the other, so the operator executes the first observable until it isn't completed, then it passes to the second observable, and so on.
This operator is used when we have in a pipe chain an operator that returns a list of observables and we need to execute them in sequence.

Converts a higher-order Observable into a first-order Observable by dropping inner Observables while the previous inner Observable has not yet completed.

import { interval, timer } from 'rxjs';
import { exhaustAll, map, take } from 'rxjs/operators';

const createSource = (index: number) => interval(index * 1000).pipe(
        take(3),
        map(value => `${index}-${value + 1}`)
    )

console.log(`${new Date().toLocaleTimeString()}: exhaustAll start`)
const source$ = timer(0, 2500).pipe(
    take(3)
);
source$.pipe(
    map(v => createSource(v + 1)),
    exhaustAll()
).subscribe({
    next: val => console.log(`${new Date().toLocaleTimeString()}: exhaustAll`, val)
});
16:27:05: exhaustAll start
16:27:07: exhaustAll 1-1
16:27:08: exhaustAll 1-2
16:27:09: exhaustAll 1-3
16:27:14: exhaustAll 3-1
16:27:17: exhaustAll 3-2
16:27:20: exhaustAll 3-3

ExhaustAll Marble Diagram

Converts a higher-order Observable into a first-order Observable which concurrently delivers all values that are emitted on the inner Observables.

import { interval, timer } from 'rxjs';
import { map, mergeAll, take } from 'rxjs/operators';

const createSource = (index: number) => interval(index * 1000).pipe(
        take(3),
        map(value => `${index}-${value + 1}`),
    )

console.log(`${new Date().toLocaleTimeString()}: mergeAll start`)
const source$ = timer(0, 1000).pipe(
    take(3),
);
source$.pipe(
    map(v => createSource(v + 1)),
    mergeAll()
).subscribe({
    next: val => console.log(`${new Date().toLocaleTimeString()}: mergeAll`, val)
});
15:41:46: mergeAll start
15:41:47: mergeAll 1-1
15:41:48: mergeAll 1-2
15:41:49: mergeAll 2-1
15:41:49: mergeAll 1-3
15:41:51: mergeAll 3-1
15:41:51: mergeAll 2-2
15:41:53: mergeAll 2-3
15:41:54: mergeAll 3-2
15:41:57: mergeAll 3-3

MergeAll Marble Diagram

Converts a higher-order Observable into a first-order Observable producing values only from the most recent observable sequence

import { interval, timer } from 'rxjs';
import { map, switchAll, take } from 'rxjs/operators';

const createSource = (index: number) => interval(index * 1000).pipe(
        take(3),
        map(value => `${index}-${value + 1}`)
    )

console.log(`${new Date().toLocaleTimeString()}: switchAll start`)
const source$ = timer(0, 1000).pipe(
    take(3)
);
source$.pipe(
    map(v => createSource(v + 1)),
    switchAll()
).subscribe({
    next: val => console.log(`${new Date().toLocaleTimeString()}: switchAll`, val)
});
15:42:48: switchAll start
15:42:49: switchAll 1-1
15:42:53: switchAll 3-1
15:42:56: switchAll 3-2
15:42:59: switchAll 3-3

SwitchAll Marble Diagram

Returns an observable that, at the moment of subscription, will synchronously emit all values provided to this operator, then subscribe to the source and mirror all of its emissions to subscribers.

import { of } from 'rxjs';
import { startWith } from 'rxjs/operators';

const source1$ = of(1, 2, 3).pipe(
    startWith(1000)
)

source1$.subscribe({
    next: val => console.log(`${new Date().toLocaleTimeString()}: startWith`, val)
});
15:43:47: startWith 1000
15:43:47: startWith 1
15:43:47: startWith 2
15:43:47: startWith 3

 

Combines the source Observable with other Observables to create an Observable whose values are calculated from the latest values of each, only when the source emits.


import { interval } from 'rxjs';
import { take, withLatestFrom } from 'rxjs/operators';

const source1$ = interval(1000).pipe(
    take(5)
)
const source2$ = interval(2500).pipe(
    take(5)
)

console.log(`${new Date().toLocaleTimeString()}: withLatestFrom start`)
source1$.pipe(
    withLatestFrom(source2$)
).subscribe({
    next: val => console.log(`${new Date().toLocaleTimeString()}: withLatestFrom`, val)
});
15:44:11: withLatestFrom start
15:44:14: withLatestFrom [ 2, 0 ]
15:44:15: withLatestFrom [ 3, 0 ]
15:44:16: withLatestFrom [ 4, 1 ]

9 - Utility Operators

I'll show you some utility operators.
tap

Used to perform side-effects for notifications from the source observable

import { of } from 'rxjs'
import { tap } from 'rxjs/operators'


of('a', 'b', 'c')
  .pipe(
      tap({
          next: x => console.log(`tap: ${x}`),
          complete: () => console.log('tap: complete'),
          error: err => console.log(`tap: error: ${err}`)
      })
  )
  .subscribe()
tap: a
tap: b
tap: c
tap: complete

This operator helps us to get some info about the observable during its execution. This operator is equal to the subscriber and it uses three methods to get info: next, complete, and error.
What could happen in these three methods? Exactly what you want :) You can run a side-effect or log the values. Usually I prefer logging info in this operator and not run side effects because at times side effects are difficult to test.

Delays the emission of items from the source Observable by a given timeout or until a given Date.

import { of } from 'rxjs'
import { delay, tap } from 'rxjs/operators'


of('a', 'b', 'c')
  .pipe(
      tap(x => console.log(`${new Date().toLocaleTimeString()} tap before delay: ${x}`)),
      delay(1000),
      tap(x => console.log(`${new Date().toLocaleTimeString()} tap after delay: ${x}`)),
  )
  .subscribe()
17:08:26 tap before delay: a
17:08:26 tap before delay: b
17:08:26 tap before delay: c
17:08:27 tap after delay: a
17:08:27 tap after delay: b
17:08:27 tap after delay: c

delay Marble Diagram
This operator is used to wait some specific time before emitting the value.

Attaches a timestamp to each item emitted by an observable indicating when it was emitted

import { of } from 'rxjs'
import { timestamp } from 'rxjs/operators'

of('a', 'b', 'c')
  .pipe(
      timestamp()
  )
  .subscribe(console.log)
{ value: 'a', timestamp: 1629385746523 }
{ value: 'b', timestamp: 1629385746528 }
{ value: 'c', timestamp: 1629385746528 }

This operator is used to attach a timestamp to each emitted item. The timestamp indicates the time when the value was emitted.
This operator can be helpful during debugging or if we need to have info about the time emission of the value.

Errors if Observable does not emit a value in given time span.

import { Observable, throwError } from 'rxjs';
import { timeout } from 'rxjs/operators';

const source$ = new Observable<number>(subscriber => {
    let id: NodeJS.Timeout | undefined
    (function next(count = 0) {
        if (count > 10) {
            subscriber.complete();
            return;
        }
        id = setTimeout(() => {
            subscriber.next(count)
            next(count + 1)
        }, Math.random() > 0.9 ? 2000 : 1000);
    })()

    return () => {
        if (id) {
            clearTimeout(id);
        }
    }

})

source$
  .pipe(
      timeout({ each: 1500, with: info => throwError(() => new Error(`Timeout ${1500}ms: info: ${JSON.stringify(info)}`)) }),
  )
  .subscribe({
      next: console.log,
      error: error => {
          console.error(`Something Wrong!`)
          console.error(error.message)
      }
  })
0
1
2
3
4
5
6
7
8
Something Wrong!
Timeout 1500ms: info: {"meta":null,"lastValue":null,"seen":9}

This operator checks the time of the execution of the observable, if the value is not emitted within the timeout time the operator throws an error.

Collects all source emissions and emits them as an array when the source completes.

import { of } from 'rxjs'
import { toArray } from 'rxjs/operators'


of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h')
  .pipe(
      toArray()
  )
  .subscribe(console.log)
[
  'a', 'b', 'c',
  'd', 'e', 'f',
  'g', 'h'
]

toArray Marble Diagram
This operator converts a sequence of values in one array, where all the array's items are the emitted values in sequence.


10 - Conditional & Mathematical Operators

I'll illustrate you two simple types of the pipeable operators: Conditional Operators and Mathematical Operators.
No time to waste, let's start.

Conditional Operators

These operators are useful to check if there are values in the observables or to find some specific value in them. Some of these operators are similar to some array methods, with the difference that they work with observables and not with arrays.

Emits a given value if the source Observable completes without emitting any next value, otherwise mirrors the source Observable.

import { EMPTY, Observer } from "rxjs";
import { defaultIfEmpty } from "rxjs/operators";

const observer: Observer<number> = {
    next: x => console.log('value', x),
    error: err => console.error('error', err),
    complete: () => console.log('complete'),
};

EMPTY.pipe(
    defaultIfEmpty(10)
).subscribe(observer);
value 10
complete

defaultIfEmpty Marble Diagram
This operator, as you can see, permits us to receive a default value if the observable does not emit any value.

Returns an Observable that emits whether or not every item of the source satisfies the condition specified.

import { Observer, of } from "rxjs";
import { every } from "rxjs/operators";

const observer: Observer<boolean> = {
    next: x => console.log('value', x),
    error: err => console.error('error', err),
    complete: () => console.log('complete'),
};

of(1,2,3,4,5,6,7,8,9).pipe(
    every(val => val < 10)
).subscribe(observer);
value true
complete

every Marble Diagram
This operator is like the every method in the arrays.
We can use it when we need to check if all the values of our observable meet a condition.

Emits only the first value emitted by the source Observable that meets some condition.

import { Observer, of } from "rxjs";
import { find } from "rxjs/operators";

const observer: Observer<number | undefined> = {
    next: x => console.log('value', x),
    error: err => console.error('error', err),
    complete: () => console.log('complete'),
};

of(1,2,3,4,5,6,7,8,9).pipe(
    find(val => val === 5)
).subscribe(observer);
value 5
complete

find Marble Diagram
This operator is like the find method in the arrays.
We can use it to find a value that meets a condition in our observable. It's important to remember that when the operator finds a value that matches our condition it completes the observable.

Emits only the index of the first value emitted by the source Observable that meets some condition.

import { Observer, of } from "rxjs";
import { findIndex } from "rxjs/operators";

const observer: Observer<number> = {
    next: x => console.log('value', x),
    error: err => console.error('error', err),
    complete: () => console.log('complete'),
};

of(1,2,3,4,5,6,7,8,9).pipe(
    findIndex(val => val === 5)
).subscribe(observer);
value 4
complete

findIndex Marble Diagram
This operator is like the findIndex method in the arrays.
We can use it to find the index of a value that meets a condition in our observable. It's important to remember that when the operator finds a value that matches our condition it completes the observable.

Emits false if the input Observable emits any values, or emits true if the input Observable completes without emitting any values.

import { EMPTY, Observer } from "rxjs";
import { isEmpty } from "rxjs/operators";

const observer: Observer<boolean> = {
    next: x => console.log('value', x),
    error: err => console.error('error', err),
    complete: () => console.log('complete'),
};

EMPTY.pipe(
    isEmpty()
).subscribe(observer);
value true
complete

isEmpty Marble Diagram
This operator is used to check if an observable has emitted at least a value.

Mathematical Operators

These operators are used to retrieve some special values in our observables or to reduce the values.

Counts the number of emissions on the source and emits that number when the source is completed.

import { Observer, of } from "rxjs";
import { count } from "rxjs/operators";

const observer: Observer<number | undefined> = {
    next: x => console.log('value', x),
    error: err => console.error('error', err),
    complete: () => console.log('complete'),
};

of("a", "b", "c", "d", "e").pipe(
    count()
).subscribe(observer);
value 5
complete

count Marble Diagram
This operator is used to count the values of our observables.

The Max operator operates on an Observable that emits numbers (or items that can be compared with a provided function), and when source Observable completes it emits a single item: the item with the largest value.

import { Observer, of } from "rxjs";
import { max } from "rxjs/operators";

const observer: Observer<string> = {
    next: x => console.log('value', x),
    error: err => console.error('error', err),
    complete: () => console.log('complete'),
};

of("a", "b", "e", "d", "c").pipe(
    max()
).subscribe(observer);
value e
complete

max Marble Diagram
This operator is used to get the maximum value emitted by our observables.

The Min operator operates on an Observable that emits numbers (or items that can be compared with a provided function), and when source Observable completes it emits a single item: the item with the smallest value.

import { Observer, of } from "rxjs";
import { min } from "rxjs/operators";

const observer: Observer<string> = {
    next: x => console.log('value', x),
    error: err => console.error('error', err),
    complete: () => console.log('complete'),
};

of("a", "b", "e", "d", "c").pipe(
    min()
).subscribe(observer);
value a
complete

min Marble Diagram
This operator is used to get the minimum value emitted by our observables.

Applies an accumulator function over the source Observable, and returns the accumulated result when the source completes, given an optional seed value.

import { Observer, of } from "rxjs";
import { reduce } from "rxjs/operators";

const observer: Observer<number> = {
    next: x => console.log('value', x),
    error: err => console.error('error', err),
    complete: () => console.log('complete'),
};

of(1,2,3,4,5,6,7,8,9).pipe(
    reduce((acc, curr) => acc + curr, 0)
).subscribe(observer);
value 45
complete

reduce Marble Diagram
This operator is like the reduce method of the array.
It can be used to reduce all the emitted values. The reduced algorithm has to be implemented by us.


11 - Error Handling Operators

One of the best practice in our work is handle the errors, so today I'll show you some operators in RxJS to handle the errors.

Catches errors on the observable to be handled by returning a new observable or throwing an error.

import { of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';

of('a', 'b', 'c', 'd', 1, 3).pipe(
    map((n: any) => n.toUpperCase()),
    catchError(err => {
        console.error(err.message);
        return of('A', 'B', 'C', 'D')
    }),
  )
  .subscribe(x => console.log(x));
A
B
C
D
n.toUpperCase is not a function
A
B
C
D

This operator is used to catch the errors during the execution of the observables.
This operator helps us to handle the errors and prevent a bad flow of our users.

import { of } from 'rxjs';
import { map, retry } from 'rxjs/operators';

function toString(val: { toString: () => string }): string | never {
    console.log('toString of', val);
    if (Math.random() > 0.6)
        return val.toString()

    console.log('toString of', val, 'failed');
    throw new Error('toString failed')
}

of(1, 2, 3, 4, 5, 6, 7, 8, 9, 0).pipe(
    map(toString),
    retry(3)
  )
  .subscribe({
      next: x => console.log(x),
      error: error => console.error(error.message),
  });
toString of 1
toString of 1 failed
toString of 1
toString of 1 failed
toString of 1
1
toString of 2
toString of 2 failed
toString of 1
toString of 1 failed
toString failed

This operator is used to retry the execution of an observable if it raises an error. We can indicate the max number of retries. If during the execution an observable raises a number of errors greater than the indicated value, the retry operator doesn't try anymore but goes out and raise the error.

Returns an Observable that mirrors the source Observable with the exception of an error. If the source Observable calls error, this method will emit the Throwable that caused the error to the Observable returned from notifier. If that Observable calls complete or error then this method will call complete or error on the child subscription. Otherwise this method will resubscribe to the source Observable.

import { interval, timer } from 'rxjs';
import { delayWhen, filter, map, retryWhen, tap } from 'rxjs/operators';

interval(1000).pipe(
  map(val => {
    if (val > 5) {
      throw val;
    }
    return val;
  }),
  retryWhen(errors =>
    errors.pipe(
      tap(val => console.log(`Value ${val} was too high!`)),
      filter((_, index) => index < 3),
      delayWhen(val => timer(val * 1000))
    )
  )
).subscribe({
    next: x => console.log(x),
    error: error => console.error(error.message),
});
0
1
2
3
4
5
Value 6 was too high!
0
1
2
3
4
5
Value 6 was too high!
0
1
2
3
4
5
Value 6 was too high!
0
1
2
3
4
5
Value 6 was too high!

This operator is used to retry the execution of an observable and it allows us to indicate the strategy of the retry.


12 - Multicast Operator

I'll show you how to share the values using the Multicast Operators.

Returns a new Observable that multicasts (shares) the original Observable. As long as there is at least one Subscriber this Observable will be subscribed and emitting data. When all subscribers have unsubscribed it will unsubscribe from the source Observable. Because the Observable is multicasting it makes the stream hot.

/**
marble share
{
    source a:           +--0-1-2-3-4-#
    operator share:     {
        +--0-1-2-3-4-#
        ......+2-3-4-#
    }
}
*/
import { interval } from 'rxjs';
import { share, take, tap } from 'rxjs/operators';

const source1 = interval(1000)
.pipe(
    take(5),
    tap((x: number) => console.log('Processing: ', x)),
    share()
);

source1.subscribe({
    next: x => console.log('subscription 1: ', x),
    complete: () => console.log('subscription 1 complete'),
});

setTimeout(() => {
    source1.subscribe({
        next: x => console.log('subscription 2: ', x),
        complete: () => console.log('subscription 2 complete'),
    });
}, 3000);


setTimeout(() => {
    source1.subscribe({
        next: x => console.log('subscription 3: ', x),
        complete: () => console.log('subscription 3 complete'),
    });
}, 7000);
Processing:  0
subscription 1:  0
Processing:  1
subscription 1:  1
Processing:  2
subscription 1:  2
subscription 2:  2
Processing:  3
subscription 1:  3
subscription 2:  3
Processing:  4
subscription 1:  4
subscription 2:  4
subscription 1 complete
subscription 2 complete
Processing:  0
subscription 3:  0
Processing:  1
subscription 3:  1
Processing:  2
subscription 3:  2
Processing:  3
subscription 3:  3
Processing:  4
subscription 3:  4
subscription 3 complete

share Marble Diagram
This operator can help us when we need to share the value of an observable during its execution. But what does it mean? It means that the first subscription starts the observable and all the next subscriptions that subscribe to this observable do not run a new instance of the observable but they receive the same values of the first subscription, thus losing all the previous values emitted before their subscription.
It's important to remember that when the observable is completed, and another observer subscribes itself to the observable, the shared operator resets the observable and restarts its execution from the beginning.
Anyway, sometimes our code needs to prevent the restarting of our observables, but what can we do in these cases?
It's simple! The share operator exposes us some options: resetOnComplete, resetOnError, resetOnRefCountZero, and each of these options can help us to prevent the resetting of the observables in different cases. These options can work or with a simple boolean value that enables or disables the behaviour, or we can pass a notifier factory that returns an observable which grants more fine-grained control over how and when the reset should happen.
The resetOnComplete option prevents the resetting after the observable's completion. So, if it is enabled when another observer subscribes to an observable already completed this observer receives immediately the complete notification.
The resetOnError option prevents the resetting of the observable after an error notification.
The resetOnRefCountZero option works with the number of observers subscribed instead. It prevents the resetting if there aren't any observer subscribed. To better understand, if all the subscriptions of our observable are unsubscribed, and this option is enabled, the observable isn't reset. otherwise, if this option is disabled, the observable restarts from the beginning at the next subscription.
Here's an example using the resetOnRefCountZero option.

import { interval, timer } from 'rxjs';
import { share, take } from 'rxjs/operators';

const source = interval(1000).pipe(take(3), share({ resetOnRefCountZero: () => timer(1000) }));

const subscriptionOne = source.subscribe(x => console.log('subscription 1: ', x));
setTimeout(() => subscriptionOne.unsubscribe(), 1300);

setTimeout(() => source.subscribe(x => console.log('subscription 2: ', x)), 1700);

setTimeout(() => source.subscribe(x => console.log('subscription 3: ', x)), 5000);
subscription 1:  0
subscription 2:  1
subscription 2:  2
subscription 3:  0
subscription 3:  1
subscription 3:  2

shared with resetOnRefCountZero option Marble Diagram

Share source and replay specified number of emissions on subscription.

import { interval } from 'rxjs';
import { shareReplay, take, tap } from 'rxjs/operators';

const obs$ = interval(1000);
const shared$ = obs$.pipe(
  take(4),
  tap(console.log),
  shareReplay(3)
);
shared$.subscribe(x => console.log('sub A: ', x));

setTimeout(() => {
  shared$.subscribe(y => console.log('sub B: ', y));
}, 3500);
0
sub A:  0
1
sub A:  1
2
sub A:  2
sub B:  0
sub B:  1
sub B:  2
3
sub A:  3
sub B:  3

shareReplay Marble Diagram
In some cases, when we share the values between multiple observers, if an observer subscribes to an already started observable, we also need to replay all the previous already emitted values. To resolve this problem we can use the shareReplay operator.
This operator shares the emitted values and if another observer subscribes to the observable it replays the previous values.
The number of values replayed can be configured: by default all the values already emitted are emitted again, but we can also indicate or a maximum number of elements to remember or a maximum time length.

#rxjs #programming #developer #javascript #react #reactiveprogramming
 

 

Everything You Need to Know About RxJS

C贸mo usar concatMap en Angular

驴Qu茅 es concatMap y cuales son sus posibles usos? Breve explicaci贸n del tema con un ejemplo b谩sico. 

#angular  #rxjs 

 

C贸mo usar concatMap en Angular