RxJS Scheduler is a powerful tool to control the execution of a subscription. A Scheduler can control when the subscription execution starts and when the notifications are delivered. You will learn when to use queueScheduler, asapScheduler, asyncScheduler and etc.

A scheduler in RxJS consists of 3 components and they are as below.

  • Data Structure – A scheduler is a data structure, it can store and queue tasks based on certain criteria.
  • Execution Context – You can control when and where the task gets executed.
  • Has a Virtual clock – The virtual clock controls when the task gets executed.

All the Observable we used and created so far has certain default behavior. RxJS assigns default scheduler to all the operator depending upon the use case. However, you can customize their default behaviors by using your own scheduler. There are different types of Schedulers in RxJS depending on the use case we have.

Types of Schedulers in RxJS

  • null – This means you are not setting any scheduler. Execution of the observable or subscription happen Synchronously.
  • queueScheduler – Schedules the execution on a queue, useful for iteration operations.
  • asapScheduler – Schedules on the JavaScript microtask queue. Microtask queue used for Promises.
  • asyncScheduler – Schedules work with setInternal.
  • animationFrameScheduler – Schedules task just before the next browser content repaint. For example, use it to create smooth browser animations.

There are also VirtualTimeScheduler and TestScheduler which are not discussed here.

The asyncScheduler in RxJS

This is the most commonly used scheduler (at least as per my knowledge). You know JavaScript engines are single-threaded and execution takes place by keeping track of event loops. This is the reason why having a blocking/synchronous execution block is bad for your Javascript code. You may often end up creating a frozen UI if you use too much of blocking (synchronous) javascript.

RxJS creators have always kept performance in priority. An asyncScheduler is used to convert a synchronous observable execution to async execution. Below is a simple example that demonstrates the use of asyncScheduler. Try on Stackblitz.com.

import { asyncScheduler, from } from "rxjs";
import { observeOn } from "rxjs/operators";

const observable$ = from([1, 2, 3, 4, 5, 6, 7, 8, 9]).pipe(
  observeOn(asyncScheduler)
);

console.log("Just before the asyncScheduler");
observable$.subscribe(val => {
  console.log("Received: " + val);
});
console.log("Just after the asyncScheduler");

Output

Just before the asyncScheduler
Just after the asyncScheduler
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Received: 6
Received: 7
Received: 8
Received: 9

As you can see the observable$ starts executing asynchronously. Async execution improves performance significantly, especially in an interactive UI application.

The queueScheduler in RxJS

The queScheduler in rxjs puts every next task in a queue instead of executing it immediately.

  • When used with delay, it behaves the same as asyncScheduler.
  • If used without a delay, it schedules a given task synchronously and executes it.
  • When a queueScheduler is called recursively, the scheduled task will wait for the current task to finish first.

The above concepts will be clear with the below-discussed examples. Remember, there are several ways to pass a scheduler to an Observable$. Most of the operators accept scheduler as the second parameter.

RxJS queueScheduler exampleStackblitz.com

import { queueScheduler, from } from "rxjs";
import { observeOn } from "rxjs/operators";

const observable$ = from([1, 2, 3, 4, 5, 6, 7, 8, 9], queueScheduler);

console.log("Just before the queueScheduler");
observable$.subscribe(val => {
  console.log("Received: " + val);
});
console.log("Just after the queueScheduler");

Output

Just before the queueScheduler
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Received: 6
Received: 7
Received: 8
Received: 9
Just after the queueScheduler

As you see in the output, queueScheduler did use a queue to execute the observable. Since there was no need to wait the above behavior is similar to the execution of a synchronous block.

The asapScheduler in RxJS

An asapScheduler performs a task as fast as it can do asynchronously.

The asapScheduler behaves similar to asyncScheduler if you use it with a delay. When you set the delay to 0, it will wait for the current synchronous block to finish execution and then start the scheduler. Run on Stackblitz.com.

import { asapScheduler, asyncScheduler, queueScheduler } from "rxjs";

asyncScheduler.schedule(() => console.log("asyncScheduler"));
asapScheduler.schedule(() => console.log("asapScheduler"));
queueScheduler.schedule(() => console.log("queueScheduler"));

Output

queueScheduler
asapScheduler
asyncScheduler

Another example of asapScheduler, asyncScheduler and queueScheduler. Stackblitz.com

import { of, merge, asapScheduler, asyncScheduler, queueScheduler } from 'rxjs';
import { filter, startWith, observeOn } from 'rxjs/operators';

const delay = 0;
const async$ = of('async scheduler')
  .pipe(observeOn(asyncScheduler, delay));

const asap$ = of('asap Scheduler')
  .pipe(observeOn(asapScheduler, delay));

const queue$ = of('queue Scheduler')
  .pipe(observeOn(queueScheduler, delay));


merge(async$, asap$, queue$)
  .pipe(filter(x => !!x))
  .subscribe(console.log);

console.log('after subscription');

Output

queue Scheduler
after subscription
asap Scheduler
async scheduler

Like I mentioned earlier, most of the time you don’t have to use schedulers. But in a tricky situation scheduler knowledge becomes crucial. Let me know your thoughts on this article in the comments below.

By |Last Updated: April 1st, 2024|Categories: RxJS|

Table of Contents