rxjs operators you should know blog cover image

RxJs Operators you should know

RxJs comes with a lot of operators. In this article, I will be discussing some RxJs operators that every developer must know and that may come handy in various scenarios.

Let’s begin learning them one by one.


timer

The timer is used to create an Observable that will emit a value after x second from creating a subscription.

import { timer } from "rxjs";

function print(value) {
  const div = document.createElement("div");
  div.innerHTML = value;
  document.body.appendChild(div);
}

const source = timer(1000);

const subscribe = source.subscribe(val => {
  print(
    `This string is shown after ${val +
      1} second from the time of creating the subscription`
  );
});

Working Demo

This can be used for executing some code after x seconds. A practical use case could be logging Analytics events.


interval

On the other hand, the interval creates an Observable that emits a value after every x seconds after a subscription is made. This operator is equivalent to setInterval in JavaScript which gets called after every specific time interval.

import { interval } from 'rxjs';

function print(value) {
  const div = document.createElement('div');
  div.innerHTML = value;
  document.body.appendChild(div);
}

const source = interval(1000);

const subscription = source.subscribe(x => print(x));

setTimeout(() => {
  subscription.unsubscribe();
}, 10000);

interval – Demo

This can be used to execute some code every x seconds. A practical use case for using interval can be Http Polling


zip

zipoperator in RxJs is used to combine two or more observables based on their index positions. Zip operator will combine the latest values of the subscriptions.

import { Observable, of, interval, zip } from 'rxjs';
import { delay } from 'rxjs/operators';

function print(value) {
  const div = document.createElement('div');
  div.innerHTML = value;
  document.body.appendChild(div);
}

const obs1 = of(1, 2, 3, 4, 5);
const obs2 = of(5, 6, 7, 8);
const obs3 = of(9, 10, 11, 12);

const obsNew = zip(obs1, obs2, obs3);

const subscription = obsNew.subscribe(val => {
  print(val);
});

zip – Demo


The obsNew will only execute after all the values at a specified value index are available for all the observables being combined.


forkJoin

While the zip in RxJs is used to combine the values of the observables based on the index positions of elements, forkJoin joins only the last values emitted by the observables only after all the joining observable completes.

import { Observable, of, interval, forkJoin } from 'rxjs';
import { delay } from 'rxjs/operators';

function print(value) {
  const div = document.createElement('div');
  div.innerHTML = value;
  document.body.appendChild(div);
}

const obs1 = of(1, 2, 3, 4);
const obs2 = of(5, 6, 7, 8).pipe(delay(2000));
const obs3 = of(9, 10, 11, 12).pipe(delay(3000));

const obsNew = forkJoin(obs1, obs2, obs3);

const subscribe = obsNew.subscribe(val => {
  print(val);
});

forkJoin – Demo

The above code will only print the value [4, 8, 12] after 3 seconds of making the subscription.

NOTE: The forkJoin operator will not work until all joining observables completes.


combineLatest

There exists another RxJs operator named combineLatest to combine two or more observables. The observable created by using the combineLatest operator emits an array of value as soon as any of the combined observable emits a new value.

import { Observable, interval, combineLatest } from 'rxjs';

function print(value) {
  const div = document.createElement('div');
  div.innerHTML = value;
  document.body.appendChild(div);
}

const obs1 = interval(1000);
const obs2 = interval(1100);

const obsNew = combineLatest(obs1, obs2);

const subscription = obsNew.subscribe(val => {
  print(val);
});

setTimeout(() => {
  subscription.unsubscribe();
}, 8000);

combineLatest – Demo

For the combineLatest to run, each of the combined observables must emit at least one value that could be combined.


concatMap

concatMap operator is used to chain Observables to execute them one after the other. A practical use case can be the scenarios where the response of an Http request is needed to execute next Http request.

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

function print(value, el) {
  const div = document.createElement('div');
  div.innerHTML = value;
  el.appendChild(div);
}

const obs1 = of(1);
const obs2 = of(2);
const obs3 = of(3);
const obs4 = of(4);
const obs5 = of(5);

obs1
  .pipe(
    concatMap(() => obs2, (x, y) => {
      // x contains the value returned by Observable obs1

      // y contains the value returned by Observable obs2
      return x + y;
    }),
    concatMap(() => obs3, (x, y) => {
      // x contains the value returned 
      // by combining Observable obs1 & obs2

      // y contains the value returned by Observable obs3
      return x + y;
    }),
    concatMap(() => obs4, (x, y) => {
      // x contains the value returned by
      // combining Observable obs1, obs2 and obs3

      // y contains the value returned by Observable obs3
      return x + y;
    }),
    concatMap(() => obs5, (x, y) => {
      // x contains the value returned by 
      // combining Observable obs1, obs2, obs3 and obs4

      // y contains the value returned by Observable obs3
      return x + y;
    }),
  )
  .subscribe((val) => {
    // final value after combing all 5 observables 
    // is received in the val parameter
    print(val, document.body);
  });

concatMap – Demo


debounceTime

debounceTime is yet another RxJs operator that lets the subscription being called after a delay from receiving the value. The debounceTime operator lets the pipe/subscription being called after a delay from receiving a new value.

The debounceTime will forward the latest value after a delay of specified time.

import { Observable, combineLatest, fromEvent } from 'rxjs';
import { tap, debounceTime } from 'rxjs/operators';

function print(value, el) {
  const div = document.createElement('div');
  div.innerHTML = value;
  el.appendChild(div);
}

// without debounceTime
fromEvent(document.getElementById('input1'), 'keyup')
.pipe(tap((event: KeyboardEvent) => {
  print((event.target as any).value, document.getElementById('div1'));
}))
.subscribe();


// with debounceTime
fromEvent(document.getElementById('input2'), 'keyup')
.pipe(
  debounceTime(800),
  tap((event: KeyboardEvent) => {
    print((event.target as any).value, document.getElementById('div2'));
  }))
.subscribe();

debounceTime – Demo

This could be helpful in scenarios where we receive multiple values and we do not want to respond to all values.


throttleTime

This operator could be useful in scenarios, where are receiving a lot of values from an Observable but we want the latest value after every x seconds. For example, when resizing the browser window, we might not want to listen to all of the resize events, but only events after every 300ms so that the performance is not wasted resizing the browser hundreds of times even for a resize drag event. throttleTime will be helpful in this scenario.

Mouse Move Event without throttleTime operator

import { Observable, fromEvent } from 'rxjs';
import { tap } from 'rxjs/operators';

function print(value, el) {
  const div = document.createElement('div');
  div.innerHTML = value;
  el.appendChild(div);
}

// without throttling
fromEvent(document, 'mousemove')
  .pipe(
    tap((event: KeyboardEvent) => {
      print('Mouse move event received', document.getElementById('div1'));
    }))
  .subscribe();

Mouse Move Event with throttleTime operator

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

function print(value, el) {
  const div = document.createElement('div');
  div.innerHTML = value;
  el.appendChild(div);
}

// without throttling
fromEvent(document, 'mousemove')
  .pipe(
    throttleTime(100),
    tap((event: KeyboardEvent) => {
      print('Mouse move event received', document.getElementById('div1'));
    }))
  .subscribe();

With throttling, you will see a lot less number of mouse move events received. This is helpful in such scenarios where we want to receive less number of events than the original events count.

 


One comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.