Waiting for things the JavaScript way…

JavaScript is filled with an abundance of libraries, frameworks, and acronyms that would make any conversation between two web developers sound like they are about to fly a spaceship to colonize Mars.
If you don’t believe me, check out this funny post:How it feels to learn JavaScript in 2016 [If this post gets a high bounce rate I suggest deleting the rest of the paragraph after Mars, on account of the link]
As such writing Async JS is no different or less confusing.

In this post I’ll try to bring clarity to asynchronous code in Javascript. I’ll focus on back-end node.js code, but a lot of it also applies to the front-end.
Let’s first cover async JS mechanisms we have in Node:

Callbacks

Promises

Generators

Async / Await

I have not included things like observers, async.js and events, as they are not exactly the core of JS. For example, events rely on an async js mechanism (such as callbacks). Many of the observer mechanisms are used mainly in front-end patterns today, and async.js is an external library which I stopped using. However if you want to learn more I suggest you look these up.

Callbacks

Callback functions are the most basic types of async code, and are common not only to Javascript but to many other languages.
Callbacks are simple to understand. Callbacks are simple functions passed as arguments, that are called when the called function is finished.

1

2

3

4

5

6

7

8

9

10

11

12

functioncallMeWhenDone(){

console.log("finished");

}

functiondoLongProcessWithCallback(param1,param2,callback){

//somelong operation

// finished

callMeWhenDone();

}

doLongProcessWithCallback("stringInput",34,callMeWhenDone);

Very simple and straightforward. The main problem with callbacks is that when these are all chained together, as many operations are in async, you’ll end up with loads of callbacks which is a nightmare to read, manage or follow. This is called callback hell.

Promises

Promises are a different way to handle asynchronus code that allows for easier managment of async code, yields easier code flow, and uses exceptions for errors, uniform signatures and easy composition, meaning we can chain promises together!

Promises are a bit like real life promises. Imagine your boss promising you a promotion next quarter. You don’t know if you’ll get it or not, and you’ll know that only in the future. Promises have three states: resolved, rejected and pending.

A promise constructor takes two parameters, reject and resolve, which will be called when the promise finishes and returns a chainable promise object.

1

2

3

4

5

6

7

8

9

constdoLongProcessWithPromise=newPromise(functionresolve(){

console.log("finished");

},functionreject(){

console.log("failed");

}

);

doLongProcessWithPromise();

This might look more complex, and for very simple situations you might be right. But let’s look at the chainable .then and .catch (for success and failure of a promise).

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

doLongProccessWithPromise()

.then(function(result){

//this is called after the promise resolves,

//and the input parameter is the return value from the success

})

//or imagine this

userSubmitPayment

.then(processPaymentInBillingSystem)

.then(processPaymentAcceptedAndApplyToOrder)

.then(generateShippingInformation)

.then(sendEmailToShippingDepartment)

.then(sendEmailToUserWithTrackingNumber)

.catch(ErrorInProcess)

As you can see this allows for chaining of promises, which creates sequential code. Sweet!

Prior to ES6 promises were supported using external libraries such as Bluebird, Q , RSVP and many others. However they are now also a part of the coding lanaguge, as promises are that important.

Promises deserve a post of their own so here is some more reading if you want to dive in and understand them better:

Generators

Generators are not designed to be an asynchronous mechanism per say. Their intent was to create an iterator-like functionality in the lanaguge; however they are often used to create cleaner looking, synchronous-like code. This is built on the fact that generators can be paused and resumed. Once again generators deserve a post of their own, so I will add additional reading links at the bottom of this section.

Generators landed in ES6, and can be created by adding a ‘*’ after the function keyword (or before, in class members):

1

2

3

4

5

6

7

8

9

10

11

function*generatorFunction(){

yield'a';//Once yield is called, the function is paused until it's called next.

yield'b';

yield'c';

}

varg=generatorFunction();"

console.log(g.next());// output: a

console.log(g.next());// output: b

console.log(g.next());// output: c

The nice thing about generators is that inside a generator function you can pass the control to another generator *yield or to a promise / value with yield:

1

2

3

4

5

6

7

8

9

function*generatorFunction(){

constuserInfo=yield getUserReturningPromise();

constorderInfo=yield*getOrdersForUserGenerator(userInfo);

returnorderInfo;

}

//wraps the generator with a promise and can now be used as a promise.

As you can see you can the code becomes simpler. You can even wrap a generator into a promise easily with a coroutine (Bluebird has a coroutine, for example).
As you can see, promises and generators co-exist nicely!

Async / Await

Async/Await is not part of ES6 sadly, but only ES7. The use of generators and promises, while nice, is not very clean. It requires a lot of wrapping, and the intent of generators was to provide an iterator, not an async mechanism. This is where async / await shines, as it is a cleaner way to handle promises and asyncronous code in a sequential manner:

All you have to do is define an async function (with the async keyword), then enter an await keyword from your promises, much like the generator yield, but with less mess:

1

2

3

4

5

6

async functiondoProcess(){

constuserInfo=await getUserReturningPromise();

constorderInfo=await getOrdersForUserPromise(userInfo);

returnorderInfo;

}

As you can see the code is clean, but didn’t require any wrapping, or using generators. Adding just two more keywords allows us to use promises everywhere (promises tend to be faster than generators).