Promises in JavaScript – What, Why and How?

For a long time I had put off learning about promises in JavaScript. I had heard that they were an alternative to callbacks i.e. they were used to manage asynchronous function calls in JavaScript. The first Node.js based app that I worked on was NodeBB and since it used callbacks I didn’t think that I needed to learn about promises. However times change.

After dabbling around in React.js I’ve figured that I have to learn about Promises if I want to be able to improve my JavaScript skills. In this post we’ll go through what promises are, what they are used for and how to use them in JavaScript.

What are Promises?

In Computer Science, Promises (also called futures, delays or deferred) are structures which are to used to manage concurrency in program execution. Many tasks could be running in parallel during program execution and Promises are used execute tasks in a series such that a second task executes only when the first completes. An example of this would be a database query. We need to make sure that a connection is established to the database first before we can query anything.

Why and How to use Promises in JavaScript?

As stated, Promises enable us to make sure that functions/tasks in JavaScript execute in a series one after the other. Consider for example the following code.

var firstName;
var lastName;

function setFirstName() {
    console.log("setFirstName called");
    firstName = "Osama";
}

function setLastName() {
    console.log("setLastName called");
    lastName = "Arshad";
}

function printName() {
    console.log("Name:", firstName, lastName);
}

setFirstName();
setLastName();
printName();

 

Here I define a very simple scenario. setFirstName sets firstName to “Osama” and setLastName sets lastName to “Arshad“. When executed, this code gives the output:

setFirstName called
setLastName called
Name: Osama Arshad

All is fine and good. But let’s say that our code had to retrieve the names from a database and the database query took around 100ms. For the sake of simplicity, I will simulate a wait using setTimeout

var firstName;
var lastName;

function setFirstName() {
    console.log("setFirstName called");
    setTimeout(function() {
        firstName = "Osama";
    }, 100);
}

function setLastName() {
    console.log("setLastName called");
    setTimeout(function() {
        lastName = "Arshad";
    }, 100);
}

function printName() {
    console.log("Name:", firstName, lastName);
}

setFirstName();
setLastName();
printName();

Now the output is

setFirstName called
setLastName called
Name: undefined undefined

See that both values are undefined. This is because JavaScript does not wait for the setTimeout in setFirstName and setLastname to complete before calling printName. This is a consequence of the asynchronous nature of JavaScript. What that means is that we cannot make JavaScript wait for completion of a function before we call another function (at least not directly). So what is the solution to this problem?

Using Callbacks

One way is to use callbacks. Callbacks are functions that are passed as arguments to functions. They are executed once a particular task is complete. Using callbacks to manage program flow, the above code becomes

var firstName;
var lastName;

function setFirstName(callback) {
    console.log("setFirstName called");
    setTimeout(function() {
        firstName = "Osama";
        callback();
    }, 100);
}

function setLastName(callback) {
    console.log("setLastName called");
    setTimeout(function() {
        lastName = "Arshad";
        callback();
    }, 100);
}

function printName() {
    console.log("Name:", firstName, lastName);
}

setFirstName(function() {
    setLastName(function() {
        printName();
    });
});

Output:

setFirstName called
setLastName called
Name: Osama Arshad

setFirstName and setLastName now receive callbacks as arguments and execute them once the setTimeouts are completed. This appears to have solved our problem.

However, this is a fairly simple example. In non-trivial applications, callbacks are notorious for causing callback hell, making it very difficult to manage code where a lot of functions are dependent upon each other.

Using Promises

To avoid callback hell, we will use Promises. We can re-write the above code as:

var firstName;
var lastName;

var setFirstName = new Promise(function(resolve, reject) {
    console.log("setFirstName called");
    setTimeout(function() {
        firstName = "Osama";
        resolve();
    }, 100);
});

var setLastName = new Promise(function(resolve, reject) {
    console.log("setLastName called");
    setTimeout(function() {
        lastName = "Arshad";
        resolve();
    }, 100);
});

function printName() {
    console.log("Name:", firstName, lastName);
}

setFirstName
    .then(setLastName)
    .then(printName);

Output:

setFirstName called
setLastName called
Name: Osama Arshad

Each Promise object takes a function as input. That function takes two callbacks, resolve and reject. resolve is called when the code in the Promise executes successfully while reject is called if any error occurs. Promises are chained together using .then() calls. In this way, any Promise in the .then chain will only execute once the previous Promise has resolved. We can chain together an indefinite number of Promises together to perform actions in sequence.

We can also illustrate another behavior. The resolve callback takes a parameter which is passed to the next thenable in the chain. Using this property we can re-write this as

var setFirstName = function() {
    return new Promise(function(resolve, reject) {
        console.log("setFirstName called");
        setTimeout(function() {
            firstName = "Osama";
            resolve(firstName);
        }, 100);
    });
};

var setLastName = function(firstName) {
    return new Promise(function(resolve, reject) {
        console.log("setLastName called");
        setTimeout(function() {
            Name = firstName + " " + "Arshad";
            resolve(Name);
        }, 100);
    });
};

function printName(Name) {
    console.log("Name:", Name);
}

setFirstName()
    .then(setLastName)
    .then(printName);

Hopefully after reading this you at least have a basic idea Promises and their use. If you notice any typos in the article, feel free to get in contact.