Guide to promises in Node.js
Tags: node
Published: 2022-07-20 15:39:13

**Written by [Pascal Akunne](https://blog.logrocket.com/author/pascalakunne/)✏️**

In Node.js applications, it’s not unusual to see a large number of nested callback functions being used to accomplish several activities. This is commonly referred to as callback hell, as it can make the code extremely complicated and disorganized. 

Fortunately, there’s a [JavaScript solution called promises](https://blog.logrocket.com/improve-async-programming-with-javascript-promises/) that solves the callback hell problem. This article will provide an overview of JavaScript promises and demonstrate how to use promises in Node.js with the `promisfy()` function. 

In this article, we’ll review the following:

*   [What is a promise?](#what-promise)
*   [How do promises work?](#how-promises-work)
*   [Creating a custom promise](#creating-custom-promise)
*   [Consuming a promise](#consuming-promise)
*   [Chaining promises](#chaining-promises)
*   [Node.js promisfy() method](#nodejs-promisfy-method)

## Prerequisites

In order to follow along, you should have the following:

*   npm and Node.js installed
*   Basic understanding of JavaScript
*   VS Code installed, or the IDE of your choosing

## What is a promise? <a name="what-promise">

A promise is essentially an improvement of callbacks that manage all asynchronous data activities. A JavaScript promise represents an activity that will either be completed or declined. If the promise is fulfilled, it is resolved; otherwise, it is rejected. Promises, unlike typical callbacks, may be chained.

## How do promises work? <a name="how-promises-work">

[JavaScript promises have three states](https://blog.logrocket.com/improve-async-programming-with-javascript-promises/#creating-promises): pending, resolved, and rejected. 

The pending state is the initial state that occurs when a promise is called. While a promise is pending, the calling function continues to run until the promise is completed, returning whatever data was requested to the calling function. 

When a promise is completed, it ends in either the resolved state or the rejected state. The resolved state indicates that the promise was successful and that the desired data is passed to the `.then()` method. 

The rejected state indicates that a promise was denied, and the error is passed to the `.catch()` method.

## Creating a custom promise <a name="creating-custom-promise">

Promises are generally created by calling a `Promise` constructor, which accepts a single callback function as an argument. The callback function, also known as the executor function, is executed immediately after a promise is created. 

The executor function accepts two callback functions as arguments, `resolve` and `reject`, which are referred to as function references. The `resolve()` and `reject()` functions each accept one argument, which could be a string, integer, Boolean, object, or array. 

To better understand how to create a custom promise, let’s look at the file, `script.js`:

```javascript
function getSumNum(a, b) {
  const customPromise = new Promise((resolve, reject) => {
    const sum = a + b;

    if(sum <= 5){
      resolve("Let's go!!")
    } else {
      reject(new Error('Oops!.. Number must be less than 5'))
    }
  })

  return customPromise
}
```

Here, we define the function `getSumNum()` to compute the sum of two integers, `a` and `b`. Within the function, we use the promise constructor, `new Promise()`, to generate a new promise. 

Next, we compute the `sum` of `a` and `b`. The `resolve` callback is executed if the `sum` is less than or equal to `5`. Otherwise, the `reject` callback is called. 

The new promise is passed to the `customPromise` variable, which is then returned. In the example above, we return a string, but it could also be an object or an array. 

Now that we understand how a promise is created, let’s review how it is consumed.

## Consuming a promise <a name="consuming-promise">

In application development, it’s much more common to consume promises than it is to create promises. 

For example, when we request data from a server via an API that returns a promise, we utilize the `then()` and `catch()` methods to consume whatever data is delivered.

```javascript
promise.then(data => {
  console.log(data)
})
.catch(err => {
  console.log(err)
})
```

In the above code, the `then()` method is executed when the promise is fulfilled by the `resolve()` callback. The `catch()` callback is called if the promise fails, passing the error of `reject()`. 

Now, let’s consume the promise we created previously:

```javascript
function getSumNum(a, b) {
  const customPromise = new Promise((resolve, reject) => {
    const sum = a + b;

    if(sum <= 5){
      resolve("Let's go!!")
    } else {
      reject(new Error('Oops!.. Number must be less than 5'))
    }
  })

  return customPromise
}

// consuming the promise
getSumNum(1, 3).then(data => {
  console.log(data)
})
.catch(err => {
  console.log(err)
})
```

The sum of one and three is less than five, so the `resolve()` callback is run. This, in turn, executes the `then()` method. If we change the parameters to result in a sum greater than five, the `reject()` callback will be run and an error will be thrown using the `catch()` method. 

Now, let’s run the following command and then check the console:

```javascript
node script.js
```

## Chaining promises <a name="chaining-promises">

Promises can be used to execute a series of asynchronous tasks in sequential order. [Chaining multiple `then()` methods to a single `Promise` outcome](https://blog.logrocket.com/understanding-promise-all-in-javascript/) helps avoid the need to code complicated nested functions (which can result in callback hell). 

To demonstrate chaining promises, let’s utilize the previous code with a few modifications:

```javascript
let value;

function getSumNum(a, b) {
  const customPromise = new Promise((resolve, reject) => {
    const sum = a + b;

    if(sum < 5){
      resolve(sum)
    } else {
      reject(new Error('Oops!.. Number must be less than 5'))
    }
  })

  return customPromise
}

getSumNum(1, 3)
.then(data => {
  console.log("initial data: " + data)
  value = data + 1 // modifying the returned data

  return value
})
.then(newData => {
  console.log("modified data: " + newData)
})
.catch(err => {
  console.log(err)
})
```

Here, we see the result is passed through a chain of `then()` methods. We begin by declaring an empty variable called `value`. This time, instead of resolving a string, we pass the `sum` value. 

When the initial promise object resolves, the `then()` function is invoked to log the initial data to the console before modifying the data by adding `1` and then assigning the resulting sum to the `value` variable. The `value` variable is passed to the next `then()` method, where the data is logged to the console. 

Now, let’s run the following command:

```javascript
node script.js
```

Here’s the output:

```
initial data: 4
modified data: 5
```

## Node.js `promisfy()` method <a name="nodejs-promisfy-method">

Promisification refers to a transformation. It is the conversion of a callback-accepting function into a promise-returning function. Promisification aids in dealing with callback-based APIs while maintaining code consistency. 

Node.js has an inbuilt utility module, `util.promisify()`, that enables the creation of flexible promisification functions in JavaScript. `util.promisify()` takes a single function parameter, which contains the callback-based function. 

Let’s look at an example to better understand how to create a promisification function in Node.js. 

First, we create two files, `promisify.js` and `promise.txt`. 

In the `promise.txt` file, we add the following text:

> Promisification refers to a transformation. It is the conversion of a callback-accepting function into a promise-returning function. Promisification aids in dealing with callback-based APIs while maintaining code consistency.

Next, we add the following code to the `promisify.js` file:

```javascript
// Importing the fs module
const fs = require('fs');
// Importing util module
const util = require('util');
// Use promisify to fs.readFile to promise based method
const readFile = util.promisify(fs.readFile);
readFile('./promise.txt', 'utf8') // Reading the .txt file
.then((text) => {
console.log(text);
})
// Log error if any
.catch((err) => {
console.log('Error', err);
});
```

To read the files in the above example, we utilize the `fs` module. Then, we use the `util.promisify()` technique to transform the `fs.readFile` into a promise-based function. Instead of a callback, the above method now returns a promise. 

Now, let’s run the following command: `node promisify.js` 

We see that the text from the `promise.txt` file is logged to the console: ![Text from Promise File](https://blog.logrocket.com/wp-content/uploads/2022/07/text-from-promise-file.png)

## Conclusion

When developing Node.js applications, it’s important to understand how to make optimal use of promises. Compared to the usual callback function, promises provide a clearer, more flexible, and better organized manner of managing asynchronous operations. 

In Node.js, we can use the `util.promisify()` utility module to easily transform a standard function that receives a callback into a function that returns a promise.

---
##200’s only :heavy_check_mark: Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, [try LogRocket.](https://logrocket.com/signup/)

[![LogRocket Sign Up](https://blog.logrocket.com/wp-content/uploads/2019/12/network-request-filter-2-1.png)](https://logrocket.com/signup/)

[LogRocket](https://logrocket.com/signup/) is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.