JavaScript Promises Explained | JavaScript Basics

Hey geeks! This week we are looking into Promises in JavaScript! Let’s say you are building a weather app. You have access to an awesome weather API you found on the web and you have decided to use it in your app. In order to accomplish the task, you have to use AJAX or Fetch API.

Consider that fetching the weather may not be instantaneous, i.e, it takes time. How does Fetch API make sure your app doesn’t stop working in the time the weather info arrives? By using Promises! Let’s dive into Promises in JavaScript and find out what it is all about.

Asynchronous Code – A Primer

Before we dive into Promises in JavaScript, we will need a solid understanding of what asynchronous code is. Normally, a program’s code runs straight through and produces a result. But if a function depends on a task that takes time to complete, the program freezes before the said function finishes its task and returns.

Blocking Code

The situation stated above is known as a blocking code. In web programming, this is a very frequent problem where the browser gets stuck while processing a request. Want a demo? Head over to this experiment hosted by MDN and try it out!

Click on the button Fill Canvas and you’ll see how the browser doesn’t respond when you click on the ‘Click me for an alert’ button. The alerts will only appear after the canvas is filled.

Related: Arrow Functions in JavaScript Explained

Threads

When you run some JavaScript code, it runs on a single thread. What are threads you may ask? A thread is simply a single process that a program uses to run. When JavaScript faces blocking code, the thread pauses execution waiting for the blocking code to finish. This is also known as synchronous programming.

In asynchronous programming, we create a second thread apart from the one our program was running on. We then assign the blocking task to the second thread, letting the main thread continue executing. Once the second thread finishes executing, the parent thread then uses the information to complete the task.

Promises in JavaScript

So how do we achieve asynchronous programming in JavaScript? We use Promises. Let’s learn by example. The constructor syntax for a promise object is:

let promise = new Promise(function(resolve, reject) {
  // execute time consuming task
});

Here, we have a Promise constructor, which is being sent a function called the executor function. Inside this function, you write the blocking code, which in our case is going to be the request for weather data.

Resolve, Reject

The executor function mentioned above is taking two arguments – resolve and reject. These are callback functions, one of which the executor will call, depending on the consequence of the blocking code.

As you may have guessed already, the executor may complete its task successfully or encounter an error. On success, the executor will call resolve() with the result obtained and on the other hand, in case of failure, it will call reject() with the error.

resolve(value) // called upon successfully finishing the task
reject(error) // called upon encountering error

Related: JavaScript Let and Const Explained

The snippets below show examples of resolve and reject:

Resolve

let promise = new Promise(function(resolve, reject) {
  // executor function

  // after 2 seconds signal that the job finished with the result "done"
  setTimeout(() => resolve("done"), 2000);
});

Reject

let promise = new Promise(function(resolve, reject) {
  // after 2 seconds signal that the job is finished with an error
  setTimeout(() => reject(new Error("Whoops!")), 2000);
});

Consumers: then, catch, finally

Once you create and define a Promise, you have to register to it in order to do something once the promise turns up with a result. This is done using three kinds of consumers: then, catch and finally. They are nothing but methods of the promise object.

.then() – This method is the most important one. It is used to handle the outcome of the promise. The syntax is as follows:

promise.then(
  function(result) {
    /* handle a successful result */ 
  },
  function(error) { 
    /* handle an error */ 
  }
);

It takes two functions as arguments. The first one handles the result and does something with it whereas the second function handles errors if they occur. We will see the complete example code in a moment.

Related: What is JSON (JavaSript Object Notation)?

.catch() – This method can be used to handle errors only. This syntax is as simple as:

promise.catch(alert(err));

.finally() – This method is rather interesting. If present, it will be executed in both cases – resolve or reject. It is similar to “finally” in a regular try {…} catch {…}. A good use case can be, say, stopping the *loading* indicator.

Live Examples

Let’s see this in action, shall we? Below you will find two blocks of code. The first block calls resolve to successfully complete a sample blocking task and the second calls reject with a fabricated error.

Press the Run button for both cases to see them in action.

Resolving A Promise

Press Run and observe the alert box which appears two seconds later with the message “done!”. The second function handling errors doesn’t run as the promise resolves.

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("done!"), 2000);
});

// resolve runs the first function in .then
promise.then(
  result => alert(result), // shows "done!" after 2 seconds
  error => alert(error) // doesn't run
);

Rejecting A Promise

Press Run and observe the alert box which appears two seconds later with the message “Whoops!”. The first function doesn’t run as the promise rejects with a fabricated error.

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("Whoops!")), 2000);
});

// reject runs the second function in .then
promise.then(
  result => alert(result), // doesn't run
  error => alert(error) // shows "Error: Whoops!" after 2 seconds
);

Related: JavaScript Keyboard Input Events Explained

Using Finally On A Promise

Press Run and observe the two alert boxes that appear. The first alert box is shown by finally displaying the message “Promise ready”. The second function doesn’t run as the promise rejects with a fabricated error.

The second alert box then appears displaying the fabricated error, triggered by the function passed as an argument to the catch() method.

new Promise((resolve, reject) => {
  throw new Error("error");
})
  .finally(() => alert("Promise ready"))
  .then(result => alert(result)) // doesn't run
  .catch(err => alert(err));  // <-- .catch handles the error object

We hope you found this tutorial helpful. Stuck, have issues or have comments? Be sure to use the Disqus thread below! We will be happy to help. 😃