Observables Introduction

Vinayraj Singh
6 min readApr 26, 2020

Observables are lazy Push collections of multiple values.

This is the definition of Observables from RxJS official website. What does this mean? … lets decode it.

1. Observables are collections of data similar to Arrays. However, they can produce values over time, either synchronously or asynchronously.

2. Observables are lazy. This means, observables do not start emitting values until they are subscribed to. Like, when we subscribe to some youtube channel then we start getting notifications.

3. Observables are Push collections i.e whenever a new value is produced by an obervable, it immediately pushes this value to its consumer.

4. Observable can produce zero or more values over time.

Metaphorically, an array can be thought of a sliced bread pack where each slice of bread represents a single value in an array.

Array metaphor

Similarly, an Observable can be metaphorically represented by an automatic chapati (flatbread) making machine that produces chapatis over time.

Observable metaphor

The above shown machine will not produce chapatis until the machine is switched-on by pressing a start button. “Pressing start button” is analogous to “subscribing to an Observable”. An Observable will start producing values as soon as it is subscribed. That’s why, Observables are called lazy.

Example of Observable:

import { Observable } from 'rxjs';// Producer
const observable = new Observable(observer => {
observer.next(1);
setTimeout(() => {
observer.next(2);
}, 1000);
setTimeout(() => {
observer.next(3);
observer.complete();
}, 2000);
});
// Consumer
const observer = {
next(x) { console.log('Next value: ' + x); },
error(err) { console.error('Error occurred: ' + err); },
complete() { console.log('Complete'); }
}
// An observer subscribing to an Observable
observable.subscribe(observer);

So…why were Observables introduced?

To understand this, lets first understand the Push and Pull mechanisms.

Pull vs Push

Pull and Push are two different type of mechanisms that describes how Producer and consumer of data communicate with each other.

In a Pull based system, the consumer asks/requests for data one at a time from the producer and then producer have to hand over that data to the consumer. It’s the consumer that decides when to receive the data, not the producer. The producer have no idea when it is going to hand over the data. It will keep waiting until being asked by the consumer for data.

In a Push based system, the producer decides when to send data to the consumer. The consumer has no idea when it is going to receive data.

Therefore, in a pull system the consumer is in control and, in a push system the producer is in control.

Examples of Pull systems

A JavaScript function is a single value pull system. The function itself is the producer of data and the code that calls/invokes the function is the consumer of that data. The consumer pulls the data by invoking the function and the producer returns data which is a single value, using the return keyword.

// Producer (of single value)
function sayHello() {
return 'Hello';
}
const s = sayHello(); // Consumer
console.log(s); // Output: Hello

JavaScript Iterator is a multi value pull system based on Iterator design pattern. We can get an Iterator object from any iterable type such as String, Array, Map, Set etc. This iterator is the underlying technology used to implement for…of loop.

So, lets say we want to consume data from an array sequentially one by one. We can do so by calling the @@iterator method of array and get an iterator object as shown below.

var arr = [1,2,3]; 
var arrIterator = arr[Symbol.iterator](); // Producer

Here, the arrIterator is the producer of data and the code that calls the next() function on the Iterator object (i.e arrIterator) is the consumer.

console.log(arrIterator.next());  // Consumer
// Output: { value: 1, done: false }
console.log(arrIterator.next()); // Consumer
// Output: { value: 2, done: false }
console.log(arrIterator.next()); // Consumer
// Output: { value: 3, done: false }

The above code acts as consumer. This is how by calling next() function data can be consumed one after the other until the producer says that there are no more values to return. The output is an object containing the value that the consumer asked for and a boolean value indicating whether the producer is done producing values or not. Once all the values has been consumed then calling next method will produce below output in which value is undefined and done is true indicating that there are no more values available.

console.log(arrIterator.next());  // Consumer
// Output: { value: undefined, done: true } -> done is true indicating no more values available for consumption

Examples of Push systems

A Promise is a most common example of a single value push system. The promise itself is a producer and the handlers registered with it are the consumers. Here, the promise controls when the value is going to be pushed into the callback function, like after one second in below example.

new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // Producer
}).then(function(result) { // Consumer
alert(result); // 1
})

Another example is an Observable.

An Observable is a new Push system introduced by RxJS. An Observable is a Producer that can produce 0 to infinite number of values over time, “pushing” them to its Consumers called Observers.

Observables were introduced to fill the missing spot of multi value push system.

It’s named observable because it gets its conceptual foundation from the observer software design pattern.

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

UML Diagram for Observer Pattern (Source: Wikipedia)

But one can argue that we have been doing something similar using the EventListener. Like below:

const buttonElement = document.getElementById('btn');// Add a handler for the 'click' event.
// Whenever the button is clicked, a pop-up with "Button clicked!" message
// will appear.
buttonElement.addEventListener('click', function (event) {
alert('Button clicked!');
});

Although, this approach could be suitable for events because event stream can never end, but in javascript we deal with different types of push streams that end/complete all the time. For example, Node streams and XMLHttpRequest.

Therefore, Observables offers much more than EventListeners.

Observables were introduced to fulfill the missing symmetry between the Iterator and Observer design patterns, which is the ability of a producer to tell the consumer that:

there are no more values to deliver

or

an error has occurred

Another important thing that Observables does for us is that, it provides us a common interface to model or adapt all the different types of push streams into streams of Observable type. And this makes modifying and combining streams a piece of cake. RxJS provides a rich set of Operators for this purpose.

These features make Observables a very powerful tool for asynchronous programming.

Conclusion

In this article, we understood what Observables are and why were they introduced. I hope you enjoyed the article.

--

--

Vinayraj Singh

Hi, I am Vinay. I am a professional software developer with a keen interest in programming.