Promises, Generators and Observable in JavaScript

In this story we will look into different ways for performing asynchronous operation in JavaScript.

JS itself actually never had direct asynchronous mechanism, surprising isn’t it? JS runs in hosting environments like browsers, NodeJS. This environments provides mechanism to executing different JS code pieces over the time called Event Loop. It is nicely explained in this video.

Why do we need Asynchronous execution of code? Imagine if there is synchronous execution, in that case browser would freeze for the period of time some JS code is executing especially code like Ajax requests, delay execution, etc. User can not perform any activity — not even scrolling while it is being done. Also, it is necessary to execute some code at arbitrary times like when Ajax request is completed.

Callbacks

The most common mechanism used to achieve asynchronous execution is callback function. Callbacks are functions passed as parameter to another functions, these functions usually performs some asynchronous activity and the callback is run when the activity is completed. Example:

// Sample ajax demo
Ajax({ url, success: function successCallback(data) {} });

Thus, Callback is function which is executed when required activity is completed. Now, consider the situation you want to do Ajax request and based on the response you are calling appropriate callbacks:

function completeOrder(url, processData, complete) {
Ajax({
url,
success: function process(data) {
processData(data, complete);
}
}
});
}

As number and depth of callbacks increases, it becomes difficult to understand the code — the ‘callback hell’. This is one issue, another issue is complete control of execution is given to the function, what if callback is never called or called where/when it shouldn’t have been called. It is difficult to take corrective action if there is exception in deep stack of callbacks.

Promises

If the functions had mechanism to return something that will let us know when the task is completed then caller code can execute required code after it. This way it will solve problems with callbacks. This is exactly what Promise does. First, lets look at above code with promises:

function completeOrder(url, processData, complete) {
Ajax({ url })
.then(data => processData(data))
.then(complete());
}

Here multiple then functions are chained together — they are called one after. When Ajax is completed then first then is called and so on.

The then function can take two function arguments —first for fulfillment and second for rejection. The promise once resolved it becomes immutable & can be then observed multiple times. Below is example of how to create new promise:

function getPromise(){
return new Promise(function(resolve, reject){
var validateResult = validate();
if (!validateResult.isValid){
reject(validateResult);
}

// do some work, it can be asynchronous
setTimeout(() => {
resolve('some data');
}, 1000);
});
}
getPromise().then(x => console.log(x));

The promise is resolved/rejected only once and then then function is called. If there are multiple call to reject/resolve only first is considered.

Generators

Generators are special functions with mechanism to input and output values using next and yield. Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances. Consider the below sample:

function* sum(input) {
var result = input + (yield);
return result;
}
var it = sum(2);// sum execution starts
it.next();
var res = it.next(4);
res.value; //6

when first next is called, generators start executing. It pauses when it encounters yield statement. When we call next again, it resumes execution with value passed in next as result of yield. It is not required to pass value for yield statement always. Below is sample Ajax request with generator, promises is also used to demonstrate how promised based libraries can be used with generators:

function callAPI(name) {
var url = 'https://someAPI?name=' + name;
return new Promise(function(resolve, reject) {
Ajax({
url,
success: function(data) {
resolve(data);
}
});
});
}
function* main() {
try {
var data = yield callAPI('Hello');
console.log('Data: ' + data);
} catch (err) {
console.error(err);
}
}
var it = main();
var res = it.next();
res.value.then(data => it.next(data));

When it.next is called, it will call API with hello as name parameter. With second call for it.next in then, the result data is yielded to data in main function.

The entire structure might look complicated, but if focus only on *main function, we can see - we are calling API and getting result in data as if it were synchronous call. There is only addition of the yield in it.

Observable

Observable represents the idea of an invokable collection of future values or events. There are wide range of operators in RXJS that helps in controlling the event flow and transforming the values and they are pure functions. Currently (2018), observable are not native in JS and it is available in RxJS library.

The main properties of observable are:

1. Lazily evaluated

2. Can be Synchronous or Asynchronous

3. Returns zero to multiple values

4. Can return values over the time

Below is Simple example of creating and using Observable. The subscribe method is required to start execution:

import { Observable } from 'rxjs';const observable = Observable.create(function(observer) {
// Get Value synchronously
observer.next(100);
// Get Value Asynchronously
setTimeout(() => {
observer.next(200);
}, 1000);
});
observable.subscribe({
next: value => console.log(value),
error: error => console.error(error),
complete: () => console.log('Done !')
});

In generators, we have used it.next to pull next value. In observable, we just subscribe and it will automatically push new value when it is produced.

Also, it is easy to cancel subscription — by calling unsubscribe on observable subscription. Example — below interval generates sequence of infinite numbers with delay specified. However, by calling unsubscribe after 4s, it will automatically cancel the further execution.

import { interval } from 'rxjs';const observable = interval(1000);
const subscription = observable.subscribe(x => console.log(x));
setTimeout(() => {
subscription.unsubscribe();
}, 4000);

Below is incomplete snippet of how observable makes it easy to call API in angular HttpClient:

getComments(){
this.httpClient.get<Comments[]>(this.commentsUrl)
.subscribe(comments => this.comments = comments);
}

Thus, observable produces multiple values like generators while promise can either resolve or reject only one values. But it has powerful feature of promise to push value. Also, Observable has functionality to subscribe and unsubscribe to start and cancel the execution.

Thanks for reading, feel free to share and tap on clap button, if you enjoyed it.

--

--

--

Full stack .Net developer, Web developer and Web Surfer.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Delay unmounting of the component in React

Getting started with Cloud Firestore on React Native

PHP basic OOP from Magic Function to Date Time

Full Stack TigerGraph Part 1

Mocha starter project

A scalable Vue2 PWA boilerplate with Vuetify & Vue I18n

Starting from Scratch Part 1: Building the Frontend with React.

Starting a new SPA project

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Chirag Rupani

Chirag Rupani

Full stack .Net developer, Web developer and Web Surfer.

More from Medium

(DS&A) JavaScript Queue

JavaScript Series (03) — handle the undefined

Higher-Order Functions and Array Methods in JavaScript(forEach, map, filter, reduce and sort)

Event Delegation in JavaScript