What is util.promisify in Node.js?

This article explains the function of the util.promisify method in Node.js, detailing how it converts traditional callback-based asynchronous functions into Promise-based functions. You will learn why this utility is essential for modern JavaScript development, how it simplifies asynchronous code, and how to implement it using practical code examples.

In older versions of Node.js, asynchronous operations relied heavily on the “error-first callback” pattern. In this pattern, functions accept a callback function as their final argument, which is executed once the asynchronous operation completes. While effective, deeply nested callbacks often led to “callback hell,” making code difficult to read, maintain, and debug.

To solve this, Node.js introduced util.promisify in version 8.0.0. This built-in utility automatically wraps standard callback-based functions and transforms them into functions that return a JavaScript Promise. This allows developers to write cleaner asynchronous code using .then() and .catch() chains, or the highly readable async/await syntax.

How it Works

For util.promisify to work, the original function must follow the standard Node.js callback convention: 1. The callback must be the last argument of the function. 2. The callback’s first parameter must be an error object (or null/undefined if successful), and the second parameter is the returned data.

Here is a comparison of reading a file using the traditional callback approach versus the promisified approach:

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

// Traditional Callback Approach
fs.readFile('example.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('Error reading file:', err);
    return;
  }
  console.log('Callback Data:', data);
});

// Promisified Approach
const readFilePromise = util.promisify(fs.readFile);

// Using Promises with async/await
async function readMyFile() {
  try {
    const data = await readFilePromise('example.txt', 'utf8');
    console.log('Promise Data:', data);
  } catch (err) {
    console.error('Error reading file:', err);
  }
}

readMyFile();

By converting the callback-based fs.readFile function with util.promisify, the code can leverage async/await. This eliminates deeply nested code, improves error handling through standard try/catch blocks, and makes the asynchronous flow look and behave like synchronous code.