Karan Jariwala
Jul 23, 2017 · 5 min read

Modern Asynchronous JavaScript — from callbacks to promise and now awaiting the async.

As async-await is now on its way to officially be a part of the ECMAScript, I started reading about it’s use-cases. And why do we need it? When reading about Promises, I had the same question: why do we need Promises when we have Callbacks? So, let’s start bottom up.

Now, what is a Callback? A Callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time.

It works in simple cases like these:

var fs = require("fs");

fs.readFile('input.txt', function(err, data) {
if (err) return console.error(err);
console.log(data);
});

But in real life where chaining multiple async calls and error handling for each success/failures is common, Callback pattern will result in large pyramid like code which is popularly known as Callback-hell.

fs.readdir(source, function (err, files) {
if (err) {
console.log('Error finding files: ' + err)
} else {
files.forEach(function (filename, fileIndex) {
console.log(filename)
gm(source + filename).size(function (err, values) {
if (err) {
console.log('Error identifying file size: ' + err)
} else {
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach(function (width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
if (err) console.log('Error writing file: ' + err)
})
}.bind(this))
}
})
})
}
})

Enter Promises to the rescue.

What is a Promise?

A Promise is a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future.

A Promise is in one of these states:

  • pending: initial state, neither fulfilled nor rejected.
  • fulfilled: meaning that the operation completed successfully.
  • rejected: meaning that the operation failed.

A pending promise can either be fulfilled with a value, or rejected with a reason (error). When either of these options happens, the associated handlers queued up by a promise’s then method are called. (If the promise has already been fulfilled or rejected when a corresponding handler is attached, the handler will be called, so there is no race condition between an asynchronous operation completing and its handlers being attached.)

Example of Async with Promises:

let myFirstPromise = new Promise((resolve, reject) => {
// We call resolve(...) when what we were doing made async successful, and reject(...) when it failed.
// In this example, we use setTimeout(...) to simulate async code.
// In reality, you will probably be using something like XHR or an HTML5 API.
setTimeout(function(){
resolve("Success!"); // Yay! Everything went well!
}, 250);
});

myFirstPromise.then((successMessage) => {
// successMessage is whatever we passed in the resolve(...) function above.
// It doesn't have to be a string, but if it is only a succeed message, it probably will be.
console.log("Yay! " + successMessage);
});

Two main benefits the Promise pattern brings over the Callback Pattern.

Passing callbacks, later, at any point in time. This brings the ability to separate initiating AJAX operations from registering callbacks using .then().

We can solve dependent callbacks more elegantly without boiler plate code.

Are the Promises perfect or there is a room for improvement?

Though the Promises have resolved the issue of callback hell to a very good extent, it created a problem of promise chaining. Since our code is generally modular and different modules communicate with each other, we started creating a promise chain. And it’s a pain to create and return promises again and again.

It is hard to pass the state inside a Promise. How do I access previous promise results in a .then() chain?

Async-Await — But wait, lets understand what are Generators/Iterators and Iterables first.

Iterable: An object is iterable if it defines its iteration behavior, such as what values are looped over in a for..of construct. Some built-in types, such as Array or Map, have a default iteration behavior, while other types (such as Object) do not.

Iterators: An object is an iterator when it knows how to access items from a collection one at a time, while keeping track of its current position within that sequence. In JavaScript an iterator is an object that provides a next() method which returns the next item in the sequence. This method returns an object with two properties: done and value.

Generators: While custom iterators are a useful tool, their creation requires careful programming due to the need to explicitly maintain their internal state. Generators provide a powerful alternative: they allow you to define an iterative algorithm by writing a single function which can maintain its own state.

Too many definitions? An easy way to understand…

Iterable is any thing you can read one by one like a book/calendar (in programming Arrray, Object, string etc.) Iterator is what helps you keep a track of where you are on the Iterable like a bookmark for books or Reminder for calenders (Pointers in programming). Generator is simply a factory that generates iterators. Think of it as a ‘Factory of Bookmarks’ or a ‘Reminder App’ with your calendar synced.

Example of a Generator:

function* idMaker() {
var index = 0;
while(true)
yield index++;
}

var gen = idMaker();

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2

Now, change the function* with async and yield with await. You would get the async/await. It is Promise + Generator/Iterator working to change your async code to make it look more like a synchronous code.

When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.

An async function can contain an await expression, that pauses the execution of the async function and waits for the passed Promise's resolution, and then resumes the asyncfunction's execution and returns the resolved value.

With Promise:

function getProcessedData(url) {
return downloadData(url) // returns a promise
.catch(e => {
return downloadFallbackData(url); // returns a promise
})
.then(v => {
return processDataInWorker(v); // returns a promise
});
}

Re-writing with Async-await:

async function getProcessedData(url) {
let v;
try {
v = await downloadData(url);
} catch(e) {
v = await downloadFallbackData(url);
}
return processDataInWorker(v);
}

This makes chaining the Promises extremely easy and readable. If you still need to do parallel stuff, you can use promises. It will all work in tandem.

Karan Jariwala

Written by

JavaScript enthusiast, UI Developer @ripplingapp, Ex -@practo, working on React JS. Follow me on twitter @karanjariwala47

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade