tezvyn:

Node.js util.promisify: From Callbacks to Promises

AI-drafted, machine-checkedSource: nodejs.orgintermediate

util.promisify converts callback-based functions into Promise-based ones, letting you use async/await with older Node.js APIs. It's a bridge for legacy code following the standard (err, value) callback pattern. The footgun: it fails on non-standard signatures.

WHY IT EXISTS: Node.js was originally built around asynchronous callbacks. This often led to deeply nested, hard-to-read code known as "callback hell". Promises and async/await were introduced to solve this, but a bridge was needed to adapt the massive ecosystem of existing callback-based functions.

THE MENTAL MODEL: Think of util.promisify as a translator. It takes a function that expects to be given a callback as its last argument, like doSomething(arg, (err, result) => {}). It wraps this function and gives you back a new one that you can await, like const result = await promisifiedDoSomething(arg);. It translates the callback's err into a rejected Promise and its result into a resolved Promise.

HOW IT WORKS: When you call promisify on a function, it returns a new wrapper function. When you invoke this wrapper, it executes the original function but provides its own internal callback. This internal callback checks if its first argument (err) is truthy. If so, it rejects the Promise that the wrapper returned. Otherwise, it resolves the Promise with the second argument (value).

WHEN TO USE IT: Use it to modernize code that interacts with older Node.js APIs or third-party libraries still relying on the (error, value) callback convention. A classic example is wrapping fs.readFile. It's perfect for refactoring old codebases to use async/await without a complete rewrite, making them more readable and maintainable.

WHEN NOT TO USE IT: Do not use it on functions that already return Promises; it's redundant. Also, avoid it for functions whose callbacks don't follow the strict (error, value) signature. If a callback provides multiple success values, like (err, val1, val2), promisify will only resolve with val1. For these cases, you must write a custom wrapper. Some functions can be customized for promisify using the util.promisify.custom symbol.

ONE CANONICAL EXAMPLE: To use the older fs.readFile with async/await, you can promisify it. Without promisify, you'd need a callback: fs.readFile('file.txt', (err, data) => {}). With it, your code becomes linear: const util = require('util'); const fs = require('fs'); const readFilePromise = util.promisify(fs.readFile); async function read() { try { const data = await readFilePromise('file.txt'); } catch (err) { console.error(err); } }.

Read the original → nodejs.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.