Understanding JavaScript Promise

Understanding JavaScript Promise

JavaScript Promise is one of the must know things for a JavaScript developer. In this tutorial, you’ll learn what are JavaScript Promises, why are they used and how to use JavaScript promise.

What is JavaScript Promise ?

From a layman’s point of view, a promise is an assurance that a particular thing will happen. Now a promise can be either kept in future or it may be broken. Similarly, JavaScript promise is an assurance that something may happen in future.

From a programming perspective, a JavaScript promise is an object which may produce a result or it may produce an error. To be more specific, the promise will either get resolved with a result or may get rejected with a reason. You can attach a callback to a promise if it has been resolved or if it has been rejected.

Let’s see an example of how to write a JavaScript promise. Here is how the basic syntax for a JavaScript promise looks like:

var promise = new Promise(function(resolve, reject){
    if(/* if everthing works as expected */) {
        resolve('all good !!')
    } else {
        reject('Something is not right !!')
    }
})

When you create a promise, it will either get resolved or it will get rejected. The JavaScript promise methods takes in a callback function with a resolve and reject parameter. If all goes good the promise will get resolved with a success message else it will get rejected with an appropriate error message.

You can attach callbacks to the above created promise to execute certain code when the promise resolves or when the promise gets rejected. Here is how you do it:

promise.then(handleSuccess, handleError)

function handleSuccess(response){
    console.log('success ', response)
}
function handleError(error){
    console.log('error ', error)
}

As seen in the above code, you can attach callback to a promise using the .then() method. You have added two callbacks to the above promise. One callback to handle the resolve state and other to handle the promise rejection.

Why Use JavaScript Promise ?

JavaScript promise looks cool and all, but why do I need to use it ? Can’t I simply use our old JavaScript callbacks ? How are callbacks different from a JavaScript promise ?

In order to understand the above question, you first need to understand what are callbacks in JavaScript. Callbacks in JavaScript means functions attached to certain events or occurrences which executes when the event occurs. A simple example of a JavaScript callback is a click event callback. Here is how it looks :

document.getElementById("btn").addEventListener("click", function(){
    // code to execute when the button btn is clicked
});

Let’s write a small code to read a file in Node.js and log its content.

fs = require('fs')
fs.readFile('index.html', 'utf8', function (err,data) {
  if (err) return console.log(err);
  console.log(data);
});

As seen in the above code, you have used the fs module to read the file content. When the content of the file index.html has been read the callback function will be executed and the data will be logged.

Now once the index.html file has been read I want to read another file called index1.html. So, you need to modify the code as shown :

fs = require('fs')
fs.readFile('index.html', 'utf8', function (err, data) {
  if (err) return console.log(err);
  console.log(data);
  fs.readFile('index1.html',' utf8', function(err, data){
    if (err) return console.log(err);
    console.log(data);
  })
});

Once index1.html file has been read I want to read another file called index2.html and similarly index3.html. Here is how the modified code will look :

fs = require('fs')
fs.readFile('index.html', 'utf8', function (err, data) {
    if (err) return console.log(err);
    console.log(data);
    fs.readFile('index1.html',' utf8', function(err, data){
        if (err) return console.log(err);
        console.log(data);
        fs.readFile('index2.html',' utf8', function(err, data){
            if (err) return console.log(err);
            console.log(data);
            fs.readFile('index3.html',' utf8', function(err, data){
                if (err) return console.log(err);
                console.log(data);
            })
        })
    })
});

The above code looks like a total mess and becomes hard to maintain when the chain continues to grow. The above code that you see with nested callbacks is what is popularly known as callback hell.

Chaining JavaScript Promise

Let’s have a look at how you can write the same piece of code using JavaScript promise. For re writing the above code using promises, you first need to separate each of the file read operations into separate functions which will return a promise. Here is how the functions look :

function readFile1(){
    return new Promise(function(resolve, reject){
        fs.readFile('index1.html','utf8', function(err, data){
            if (err) reject(err)
            resolve(data)
        })
    })
}

function readFile2(){
    return new Promise(function(resolve, reject){
        fs.readFile('index2.html','utf8', function(err, data){
            if (err) reject(err)
            resolve(data)
        })
    })
}

function readFile3(){
    return new Promise(function(resolve, reject){
        fs.readFile('index3.html','utf8', function(err, data){      
            if (err) reject(err)
            resolve(data)
        })
    })
}

Now you can make a call to the first function that you want to execute and chain the rest of the functions accordingly. Here is how it looks :

readFile1()
.then(function(resp){
    console.log(resp);
    return readFile2()
})
.then(function(resp){
    console.log(resp);
    return readFile3()
})
.then(function(resp){
    console.log(resp);
})

As seen in the above code, you first make a call to the readFile1 function which returns a promise. When that promise gets resolved you make a call to the readFile2 function. Similarly, readFile2 returns a promise and readFile3 is called when the promise returned by readFile2 is resolved. This process of executing one JavaScript promise after the other is called JavaScript promise chaining.

One key thing to note here is that every call to a then returns a promise object except the last one.

Error Handling In JavaScript Promise

There are two ways to handle a promise rejection. Let’s have a look at how you typically reject a promise.

function doPromiseStuff(){
    return new Promise(function(resolve, reject){
        reject('promise rejected')
    })
}

doPromiseStuff()
.then(function(response){
    console.log('response is ', response)
}, function(error){
    console.log('error is ', error);
})

As seen in the above code, when the doPromiseStuff promise gets rejected you can handle the rejection using the second parameter of the .then callback.

Now let’s say you have a couple of more promises chained along with the existing promise. To handle promise rejection you will have to write down the .then promise rejection second parameter for each promise. So, a better way to handle rejection when dealing with promise chaining is using the .catch method of JavaScript promise.

Here is an example of promise chaining with .catch method.

function getPromiseResolve(){
    return new Promise(function(resolve, reject){
        resolve('promise resolved')
    })
}

function getPromiseRejected(){
    return new Promise(function(resolve, reject){
        reject('promise rejected')
    })
}

getPromiseResolve()
.then(function(res){
    return getPromiseRejected()
})
.then(function(res){
    console.log('response is ', res);
})
.catch(function(error){
    console.log('error is ', error);
})

You have attached a catch handler to the JavaScript promise chain to handle the promise rejection. When the first promise gets resolved, you called the second promise which returns a rejected promise which is handled in the catch block. Similarly, any promise rejection will be handled in the catch block.

Let’s say you have a number of JavaScript promises chained. You get an error then how will you identify from line the error is throwing up ?

One nice way to handle this is to reject with a new JavaScript error. Have a look a the modified code:

function getPromiseRejected(){
    return new Promise(function(resolve, reject){
        reject(new Error('promise rejected'))
    })
}

The code will log the error with a stack trace which will be helpful in debugging the code.

error is  Error: promise rejected
    at C:\Users\ajay\Desktop\index.js:57:10
    at new Promise (<anonymous>)
    at getPromiseRejected (C:\Users\ajay\Desktop\index.js:56:9)
    at C:\Users\ajay\Desktop\index.js:63:9
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
    at Function.Module.runMain (module.js:695:11)
    at startup (bootstrap_node.js:191:16)
    at bootstrap_node.js:612:3

JavaScript Promise All

At times it’s required to execute a number of promises in parallel. When you are in a scenario when you need to be sure that a certain set of promises have been executed you can use JavaScript Promise.all. Assuming that you have a couple of promises defined as shown:

function doTask1(){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve('task one resolved')
        }, 1000)
    })
}

function doTask2(){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve('task two resolved')
        }, 2000)
    })
}

function doTask3(){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve('task three resolved')
        }, 3000)
    })
}

Let’s make use of Promise.all method to execute the promise doTask1 and doTask2. Once doTask1 and doTask2 has been executed, let’s execute doTask3. Here is how you can use Promise.all() to accomplish the following task:

var promises = Promise.all([doTask1(), doTask2()])

promises.then(function(response){
    console.log(response)
    doTask3()
    .then(function(response){
        console.log(response)
    })
})

Wrapping It Up

In this tutorial, you learnt what is a JavaScript promise, how to use Promise and why use a promise. You also learnt how to chain JavaScript promises, how to handle errors when writing promises and how to use Promise.all() to execute promises in parallel.

How was your experience learning Promises ? Do let us know thoughts or suggestions in the comments below.