Click to share! ⬇️

es6 promises

Modern programming languages have a jargon all to themselves. We are inundated with acronyms and foreign-sounding words every day. Today, we’ll be talking about Promises. If you make use of our good friend Webster, you’ll find something along the lines of this for the meaning of a promise: A declaration or assurance that one will do a particular thing or that a particular thing will happen. Friends make promises, family make promises, you likely make promises to people. You know what a promise is. Now let’s think about the idea of a Promise in JavaScript. What is it? Well, put simply, a JavaScript Promise is an object that is waiting for an asynchronous operation to complete. When that operation does complete, the Promise is either fulfilled or rejected. We’ll examine this concept in this tutorial about ES6 Promises.


Returning A Promise

There are a lot of ways to look at Promises. We all might understand them a little differently depending on how our brains work. A common way to explain how a promise in ES6 works, is by setting up a function that returns a promise to inspect how things work. Have a look at this code:

function return_a_promise() {
    let promise = new Promise(function (resolve, reject) {
        console.log('Fetching data, please wait.');
        setTimeout(function () {
            console.log('Got the goods, nice work.');
        }, 2000);
    });
    return promise;
}

let promise = return_a_promise();

// Fetching data, please wait.
// Got the goods, nice work.

returning a promise
So here is a function named return_a_promise() that you guessed it, returns a promise. You can see we declare a variable named promise and we new up a Promise object. The Promise constructor accepts a function as an argument. The function passed as an argument to the Promise constructor itself takes two arguments. The first is resolve, and the second is reject. If we want the promise to be resolved and fulfilled, we can call the resolve() function. If we run into an error condition, we would instead call the reject() function. So we called neither in this first snippet yet when we inspect the console we see “Fetching data, please wait.” then after a two second delay we see “Got the goods, nice work.”

The function passed to new Promise executes immediately

What we wanted to see in this first example is simply that when you create a Promise, you need to pass it a function. In addition, that function will execute immediately. This is the reason we see the output already in the console.


Notify the promise.then() of success with resolve()

Since the function of return_a_promise() returns a promise, then we know that the promise variable is in fact a promise. Specifically it is a promise object. If you log out typeof promise you will get object and if you log out promise instance of Promise you will get true. What that means is that we now have access to some special methods on that object. The first we will look at is .then().

function return_a_promise() {
    let promise = new Promise(function (resolve, reject) {
        console.log('Fetching data, please wait');
        setTimeout(function () {
            console.log('Got the goods, nice work.');
            resolve();
        }, 2000);
    });
    return promise;
}

let promise = return_a_promise();

promise.then(function () {
    console.log('The promise is fulfilled!');
});

// Fetching data, please wait
// (2 second delay here)
// Got the goods, nice work.
// The promise is fulfilled!

promise then resolve
Notice that on the promise variable, we are now calling .then() passing it a function. All the function does is to log out “The promise is fulfilled!” What is interesting however is that we also added that call to resolve() up in the Promise constructor. See it up there? So what we are doing here is simulating a call to a server to fetch data or some type of asynchronous action. The setTimeout() function is simply there to simulate a slight delay while waiting for a resource. So after 2000 milliseconds or 2 seconds, we log out “Got the goods, nice work.” We are pretending we just got back the data we need from the server. Well, that’s great right? Since it succeeded, we should make a call to resolve(), and that is exactly what we do here. So what does resolve() actually do?? Well, when we call resolve() it actually notifies the promise of success and the first function which is passed to .then() is called. It almost like an event listener. promise.then() is just sitting around waiting for a promise to be fulfilled or rejected. Once it knows which it is, it takes the appropriate action based on what is contained in the functions passed to it. In this example here, it waits for 2 seconds, and since we have success, we call resolve() and the appropriate code runs.


Notify the promise.then() of success with reject()

Success is not always guaranteed. In those cases where we encounter a failure, we are going to need a way to handle that problem. It’s almost like dealing with an exception. You tried to do something, but there was a problem so you need to handle it. Consider this new code.

function return_a_promise() {
    let promise = new Promise(function (resolve, reject) {
        console.log('Fetching data, please wait');
        setTimeout(function () {
            console.log('Error code 500: Server Crashed.');
            reject();
        }, 2000);
    });
    return promise;
}

let promise = return_a_promise();

promise.then(function () {
    console.log('The promise is fulfilled!');
}, function () {
    console.log('The promise is rejected :-(')
});

// Fetching data, please wait
// (2 second delay here)
// Error code 500: Server Crashed.
// The promise is rejected :-(

promise then reject
This time around we are simulating a server error. So in the Promise you can see we are trying to fetch some data. After two seconds, we find that we have an error code 500. Not good. Not good at all. Since their has been a failure, we need to notify the promise of the failure. That is exactly what we do when we call reject(). So since we call reject(), it is now the second function passed to promise.then() which runs. This is the function which handles a promise rejection or error state. In this scenario, we simply log out the bad new that “The promise is rejected :-(” In summary we can see that promise.then() is an important function which handles whether a promise was fulfilled or rejected in your application. promise.then() takes two functions as it’s arguments. The first function runs if the promise is fulfilled or successful, and the second function runs if the promise is rejected or has failure.


Passing Parameters to resolve() or reject()

Now that we now how resolve() and reject() work in promises, let’s examine them a little more closely. A nice feature of these two functions is that you can pass a parameter to them. Let’s see an example.

function return_a_promise() {
    let promise = new Promise(function (resolve, reject) {
        console.log('Fetching data, please wait');
        setTimeout(function () {
            resolve('200 OK: ');
        }, 2000);
    });
    return promise;
}

let promise = return_a_promise();

promise.then(function (message) {
    console.log(message + 'The promise is fulfilled!');
}, function (message) {
    console.log(message + 'Whoops, something went wrong.')
});

// Fetching data, please wait
// 200 OK: The promise is fulfilled!

promise pass parameter to resolve
Notice that we remove the logging we had before in the promise. This time around, we simply call the resolve() function and pass it a string of ‘200 OK: ‘ as a parameter. We can now get access to this value when we call the functions inside of promise.then(). So in this example, we assume success from the server. For that reason we call resolve('200 OK: '); Do to success, the first function inside promise.then() runs. Notice we now accept a message parameter to that function. When we log out the notification, we include message as part of the notification. As such we see “200 OK: The promise is fulfilled!” in the console. Great! For the sake of completeness here is the scenario of the promise being rejected.

function return_a_promise() {
    let promise = new Promise(function (resolve, reject) {
        console.log('Fetching data, please wait');
        setTimeout(function () {
            reject('500 Internal Server Error: ');
        }, 2000);
    });
    return promise;
}

let promise = return_a_promise();

promise.then(function (message) {
    console.log(message + 'The promise is fulfilled!');
}, function (message) {
    console.log(message + 'Whoops, something went wrong.')
});

// Fetching data, please wait
// 500 Internal Server Error: Whoops, something went wrong.

promise pass parameter to reject


Chaining then() function calls

Here we refactor the code a few ways. First off, we don’t have to call let promise = return_a_promise(); to store the promise in promise. It’s just as valid to call it like so: return_a_promise().then();. In addition to this, you can actually chain on additional calls to then like so: return_a_promise().then().then();. Let’s see this concept in action here.

function return_a_promise() {
    let promise = new Promise(function (resolve, reject) {
        console.log('Fetching data, please wait');
        setTimeout(function () {
            resolve('200 OK: ');
        }, 2000);
    });
    return promise;
}

return_a_promise().then(function (message) {
    console.log(message + 'The promise is fulfilled!');
    return 'Yeah!!';
}, function (message) {
    console.log(message + 'Whoops, something went wrong.')
}).then(function (message) {
    console.log('Seriously, time to party! ' + message);
});

// Fetching data, please wait
// 200 OK: The promise is fulfilled!
// Seriously, time to party!  Yeah!!

chaining then function calls on a promise
Since the promise is successful, we call resolve(). This fires the first function passed to then(). Notice that in that function, after we log out the message, we have a line where we return “Yeah!!”. This value now becomes available to the next call to then(). See how it is chained on to the end of the first then(). When then() is called the second time, it is also accepting a message argument however instead of this value coming from resolve(), it is coming from what we returned out of the first then() call. Hopefully that makes sense! Chaining of functions is pretty common when using Promises, as well as in other aspects of JavaScript.


Handling Promise Rejection with .catch()

If a promise fails, we have seen how we can call reject() and the second function passed to promise.then() will fire. Essentially, this is how we handle error conditions in the application. An alternative method of dealing with a promise rejection is to make use of the catch() method of a promise. It takes a function that runs if the promise fails and reject() is called. Here is an example of that in action.

function return_a_promise() {
    let promise = new Promise(function (resolve, reject) {
        console.log('Fetching data, please wait');
        setTimeout(function () {
            reject('500 Internal Server Error ');
        }, 2000);
    });
    return promise;
}

return_a_promise().then(function (message) {
    console.log(message + 'The promise is fulfilled!');
}).catch(function (message) {
    console.log(message);
});

// Fetching data, please wait
// 500 Internal Server Error 

promise catch


How to chain asynchronous calls together using Promises

All of the examples so far have looked at using a Promise to complete one asynchronous call in your code. What happens if you need to chain asynchronous calls together? This is possible with Promises, and we will examine how to do that very thing in this section of the ES6 Promise Tutorial. The way you can accomplish chaining of asynchronous calls via Promises is to pass a function that returns a promise to the resolve() function of the prior Promise. Sounds sketchy? Let’s see an example.

function return_a_promise() {
    let promise = new Promise(function (resolve, reject) {
        console.log('Parsing html, please wait');
        setTimeout(function () {
            resolve(return_a_second_promise());
        }, 2000);
    });
    return promise;
}

function return_a_second_promise() {
    let promise = new Promise(function (resolve, reject) {
        console.log('Requesting CDN data, please wait');
        setTimeout(function () {
            resolve('Faster image load from CDN.');
        }, 2000);
    });
    return promise;
}

return_a_promise().then(function (message) {
    console.log(message + ' The promise is fulfilled!');
}).catch(function (message) {
    console.log(message);
});

// Parsing html, please wait (from first promise)
//   (2 second delay)
// Requesting CDN data, please wait (from second promise)
//   (another 2 second delay)
// Faster image load from CDN. The promise is fulfilled!

To help bring the concept home, we show a little screen capture here of the result happening in real-time. This gives us a better idea of how the two asynchronous calls are working.
multiple asynchronous calls with promises


Creating a Promise instantly with no asynchronous call

Sometimes when you need to make an asynchronous call, you may realize you don’t even have to make that call. This might be due to data already being available in the cache, or for some other reason. In a case like this, you can still return a promise if you like and then just make use of the static resolve or reject functions. By calling Promise.resolve(), you are creating a promise that is guaranteed to be fulfilled. Conversely, by calling Promise.reject(), you implement a Promise that is guaranteed to be rejected.

calling Promise.resolve()

function return_a_promise() {
    return Promise.resolve('Here is your data');
}

return_a_promise().then(function (value) {
    console.log(value);
}, function (reason) {
    console.log(reason);
});

// Here is your data

Calling Promise.reject()

function return_a_promise() {
    return Promise.reject('Fail!');
}

return_a_promise().then(function (value) {
    console.log(value);
}, function (reason) {
    console.log(reason);
});

// Fail!

How to use Promise.all()

Promise.all() is another static function you can use with Promises, beyond the resolve() and reject() that we are now familiar with. Let’s look at the code and output, then we can discuss.

let promise1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log('Hi from promise 1');
        resolve('Promise 1 resolved');
    }, 1000);
});

let promise2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log('Hi from promise 2');
        resolve('Promise 2 resolved');
    }, 3000);
});

Promise.all([promise1, promise2]).then(function (value) {
    console.log(value[0] + ' and ' + value[1]);
}, function (reason) {
    console.log(reason);
});

How to use Promise all

So in this example, we new up two Promises. One get’s assigned to promise1 and the other gets assigned to promise2. We set up the first promise to resolve after one second, and the next promise to resolve after three seconds. Great. After this we make a call to Promise.all() and we pass in an array that contains each promise object we had created. The way Promise.all() works is to wait until all promises are resolved, and then the first function passed to .then() will run to let us know all promises resolved. If any promise is rejected, the second function passed to .then() is immediately called.


How to use Promise.race()

This static function works in a somewhat similar way to Promise.all() in that we pass it an array of promises. The difference, however, is that Promise.race() does not wait for all promises to resolve. As soon as it sees at least one of them resolve, the .then() function is triggered. Think of it as a race to see which promises either resolve or reject first. As soon as that happens, .then() is called.

let promise1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log('Hi from promise 1');
        resolve('Promise 1 resolved');
    }, 1000);
});

let promise2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log('Hi from promise 2');
        resolve('Promise 2 resolved');
    }, 3000);
});

Promise.race([promise1, promise2]).then(function (value) {
    console.log(value);
}, function (reason) {
    console.log(reason);
});

// Hi from promise 1
// Promise 1 resolved
// Hi from promise 2

We’ll look at one last example of Promise.all() vs Promise.race() to fully understand how they work.


Promise.all() Example

let promise1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log('Hi from promise 1');
        resolve('1 worked');
    }, 1000);
});

let promise2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log('Hi from promise 2');
        resolve('2 worked');
    }, 3000);
});

Promise.all([promise1, promise2]).then(function (value) {
    console.log(value);
}, function (reason) {
    console.log(reason);
});


// Hi from promise 1
// Hi from promise 2
// ["1 worked", "2 worked"]

How to use promise all 2


Promise.race() Example

let promise1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log('Hi from promise 1');
        resolve('1 worked');
    }, 1000);
});

let promise2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log('Hi from promise 2');
        resolve('2 worked');
    }, 3000);
});

Promise.race([promise1, promise2]).then(function (value) {
    console.log(value);
}, function (reason) {
    console.log(reason);
});

// Hi from promise 1
// 1 worked
// Hi from promise 2

How to use promise race


ES6 Promises Tutorial Summary

Promises are a way to represent an eventual result of an asynchronous operation. They provide a means of handling asynchronous processing in a more synchronous way. A promise is the representation of a value that can be handled in the future, and they come with a few guarantees:

  • promises are immutable
  • promises are resolved or rejected
  • when a promise is resolved, we are guaranteed to receive a value
  • when a promise is rejected, we are guaranteed to receive the reason why the promise cannot be fulfilled

With this info in mind, our tutorial covered a lot of ground. We learned about the Promise object, and how to create a function that returns a promise. Since promises involve asynchronous calls, we used the JavaScript setTimeout function to simulate this in our many examples. We also observed that when using promises we will make use of the resolve(), reject(), all(), race(), and then() methods to handle the results of a promise. Great Work!

Click to share! ⬇️