Callback to promise-based functions
Tags: node, javascript
Published: 2021-07-28 12:39:15

---
title: Callback to promise-based functions
published: true
date: 2021-04-06 00:00:00 UTC
tags: node, javascript
canonical_url: https://www.mariokandut.com/how-to-convert-callback-based-to-promise-based-functions-node-js-promisify/
---

Promises allow to handle the results of asynchronous code, like callbacks. Though, unlike callbacks, the async code with promises is easier to read, maintain, and reason about. They make writing asynchronous code easier and are an improvement to the callback pattern. If you want to see for yourself, feel free to search for `callback hell` in your favorite search engine.

Learn more about Promises in the article [Understanding Promises in Node.js](https://www.mariokandut.com/understanding-promises-in-node-js/).

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#what-is-utilpromisify)What is util.promisify()?

Node.js has a built-in util package. This util package includes utility functions. One of these is the `promisfy()` function that **converts callback-based to promise-based** functions. With the help of this util function we are able to use promise chaining and `async/await` with callback-based APIs.

Let's try to read the `package.json` with the built-in `fs` package.

Create project folder and init with `npm init -y` to auto-generate a `package.json`.

```
mkdir node-promisfy
cd node-promisfy
npm init -y
```

Create a `index.js` file.

```
touch index.js
```

Copy the callback-based test code.

```
const fs = require('fs');

fs.readFile('./package.json', function callback(err, data) {
  const package = JSON.parse(data.toString('utf8'));
  console.log(package.name);
});
```

Try to run this code with `node index.js` and the output should be `node-promisify`.

Now, let's convert this `fs.readFile` function to be promise-based instead of callback-based.

```
const fs = require('fs');
const util = require('util');

// Convert `fs.readFile()` into a function that takes the
// same parameters but returns a promise.
const readFile = util.promisify(fs.readFile);

readFile('./package.json')
  .then(response => {
    const package = JSON.parse(response.toString('utf8'));
    console.log(package.name);
  })
  .catch(err => console.log(err));
```

Or, equivalently to `.then` handler, using an `async function` with logging the `uid` of the directory owner.

```
const util = require('util');
const fs = require('fs');

const stat = util.promisify(fs.stat);

async function callStat() {
  const stats = await stat('.');
  console.log(`This directory is owned by ${stats.uid}`);
}
callStat();
```

Since **Node.js version 10+** , the **fs library has built-in support for Promises**. The `fs.promises` API provides an alternative set of asynchronous file system methods that return Promise objects rather than using callbacks. You can access this API via `require('fs').promises` or `require('fs/promises')`.

The example for reading the `package.json` file, is much cleaner and easier to read.

```
const fsp = require('fs').promises;

fsp
  .readFile('./package.json')
  .then(response => {
    const package = JSON.parse(response.toString('utf8'));
    console.log(package.name);
  })
  .catch(err => console.log(err));
```

`promisify()` assumes that original is a function taking a callback as its final argument in all cases. If original is not a function, an error will be thrown by `promisify()`. If original is a function but its last argument is not an error-first callback, it will still be passed an error-first callback as its last argument.

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#using-promisify-on-class-methods)Using promisify on Class methods

Using `promisify()` on class methods or other methods that use `this` may not work as expected. Special handling (binding) for that case is required, see example below:

```
const util = require('util');

class Foo {
  constructor() {
    this.a = 42;
  }

  bar(callback) {
    callback(null, this.a);
  }
}

const foo = new Foo();

const naiveBar = util.promisify(foo.bar);
// TypeError: Cannot read property 'a' of undefined
// naiveBar().then(a => console.log(a));

naiveBar.call(foo).then(a => console.log(a)); // '42'

const bindBar = naiveBar.bind(foo);
```

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#custom-promisified-functions)Custom promisified functions

Using the `util.promisify.custom` symbol one can override the return value of `util.promisify()`, see more in the [Node.js docs](https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_promisify_original):

```
const util = require('util');

function doSomething(foo, callback) {
  // ...
}

doSomething[util.promisify.custom] = foo => {
  return getPromiseSomehow();
};

const promisified = util.promisify(doSomething);
console.log(promisified === doSomething[util.promisify.custom]);
// prints 'true'
```

This can be useful, if the original function does not follow the error-first callback, so if the function takes `(foo, onSuccessCallback, onErrorCallback)` instead of `onErrorCallback` first.

```
doSomething[util.promisify.custom] = foo => {
  return new Promise((resolve, reject) => {
    doSomething(foo, resolve, reject);
  });
};
```

If `promisify.custom` is defined but is not a function, `promisify()` will throw an error.

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#tldr)TL;DR

- `util.promisify` takes a function following error-first callback style `(err, value) => ...` and returns a version that returns promises.
- `promisify` can be required via the `util` package and is built-in.
- The built-in file-system `fs` module has a `fs.promises` API which returns `Promise` objects, since Node.js Version 10.
- Using `promisify()` on class methods or other methods which implement `this` requires special handling.

Thanks for reading and if you have any **questions** , use the **comment** function or **send me a message** **[@mariokandut](https://www.twitter.com/mariokandut)**.

If you want to know more about **[Node](https://www.mariokandut.com/tags/node)**, have a look at these [Node Tutorials](https://www.mariokandut.com/tags/node).

**References (and Big thanks):**

[Node.js promisify()](https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_promisify_original),[Node.js fs.promises](https://nodejs.org/dist/latest-v14.x/docs/api/fs.html#fs_fs_promises_api),[Flavio](https://flaviocopes.com/node-promisify/),[MasteringJS](https://masteringjs.io/tutorials/node/promisify)