Home Insights NodeJS Asynchronous Development
30th June 2017 in Web Development

NodeJS Asynchronous Development

Paul Wong-Gibbs

By Paul Wong-Gibbs

NodeJS Asynchronous Development

Introduction

JavaScript has made huge strides in the development world, with the addition of new and exciting features and frameworks. It’s rapid growth and popularity have made it a very promising contender for any application stack.

What is Asynchronous development?

Recently I have been writing code for small NodeJS tools and wanted to share a very popular topic, asynchronous development / Non Blocking code. Because JavaScript is a single threaded programming language it relies on it’s call stack to perform operations. Much like a queue at a supermarket, only imagine one till open, chaos! JavaScript however, has a great way of managing these operations.

Keeping with our supermarket analogy when an operation is called in JavaScript it’s added to the call stack then executed and returned much like a person going to a till paying for their groceries then leaving the store (synchronous).

Quickly this begins to breakdown as the call stack builds up, enter asynchronous development. Asynchronous code takes statements outside of the main program flow, allowing the code after the asynchronous call to be executed immediately without waiting.

A good example is the setTimeout function which is asynchronous, notice how the log is ONE, THREE, TWO. (give it a try in the console!)

console.log('ONE!');

setTimeout(() => {
    console.log('TWO!');
}, 2000);

console.log('THREE!');

// logs out: ONE! THREE! TWO!

What exactly is happening here?

Although Asynchronous implementations vary across browsers and NodeJS the concept is fairly similar I will be focusing on the NodeJS event driven implementation.

The first line console.log('ONE!'); is added to the call stack then executes logging ‘ONE!’ the setTimeout() does not have any functions but rather has a function argument known as a callback, it gets added to the call stack then popped out immediately (not logging anything). console.log('THREE!'); then gets executed and logs out ‘THREE!’.

Now the call stack is empty which means that callback we have been waiting two seconds for gets added to the call stack and executed logging ‘TWO!’.

To better understand how the callback methodology works we need to take a look at the event/message/callback queue this is different from the call stack! It’s simply put a list of events to be processed. When we store an event on the queue we sometimes store a function with it this function is what we know as the callback. A queue data structure is a first in first out structure.

When an event is added to the queue it’s waiting for the call stack to be empty so it can process these events, the part that decides when the call stack is ok to receive an event is known as the event loop.

Handling Asynchronous Functions

There are many different api’s JavaScript has for developers to consume asynchronous behaviour.

Callbacks

Functions are first class objects which means functions are the type of “object” (give it a try typeof function () {}) with this knowledge we can treat function decorations as any other first class object (String, Array, Number) this includes assignment to variables and passed as arguments to functions.

Callbacks are function decorations passed as an argument to another function. Since the argument passed is a declaration the function it was passed to can decide when it is executed with the parenthesis ().

Callback functions are often used to declare some code to be executed after a lengthy operation or event has been fulfilled this makes them perfect for use with asynchronous development.

This is particularly common in the jQuery library and a vital part of the way we develop in node.

$.each([1,2,3,4,5], function (value) {
    console.log(value);
});

Callbacks do however have a fundamental problem when it comes to making several asynchronous calls one after the other. In the programming world this is known as callback hell

Promises

The Promise object is essentially a wrapper around an asynchronous function, by wrapping our function in this object we have access to the Promise API.

A Promise is normally invoked with a callback function which has two arguments; resolve and reject these arguments are functions when called resolve or reject the promise.

It’s up to the developer to decide exactly when to call the resolve or reject within the body of the callback function.

function readAsync() {
    return new Promise((response, request) => {
        fs.readdir('./', (error, filenames) => {
            if (error) {
                reject(error);
            }
            resolve(filenames);
        })
    })
}

The promise api is “chainable” allowing developers to connect asynchronous functions together one after the other this makes for better and more readable code.

When the reject function is called within the chain .catch handles that call

getFileContents('./hello.txt')
    .then((text) => doSomethingWithTheText(text))
    .then((modifiedText) => writeBackToFile(modifiedText))
    .catch((error) => console.log(error));

There are many third party promise libraries available for JavaScript such as Bluebird, When and Q I would advise trying them out!

ASYNC and AWAIT

ES6/ES2015 offers a whole new suite of syntax for developers to work with, two of these new features are Async and await, these two new features hope to ease the use of asynchronous programming. The purpose of async/await is to simplify the behaviour of using promises in a more synchronous manor, much like promises use structured callbacks, async/await uses a combination of generators and promises.

async function write() {
    var text = await read();
    console.log(text);
}

Leave a Reply

Your email address will not be published. Required fields are marked *