Skip to solution
easyPhone Screen

What is the purpose of the util.promisify function in Node.js?

452 views
01

Understand the problem

Question presented to candidate: "You are working with an old library that only exposes callback-style APIs (error-first callbacks), and the rest of your codebase uses async/await. What is the standard way to bridge that gap without rewriting the library?"

What a strong answer should cover:

  • util.promisify(fn) takes a function following Node's error-first callback conventionfn(...args, (err, result) => {...}) — and returns a new function that instead returns a Promise, resolving with result or rejecting with err.
  • 📌 This is verifiably correct for both outcomes, not just the happy path: a promisified function whose original callback is invoked with a result resolves correctly; one whose callback is invoked with an Error produces a genuinely rejected Promise, catchable with .catch() or try/catch.
  • It requires the target function to follow the exact error-first, callback-last convention — a function with a different callback signature (callback not last, multiple non-error result arguments in a non-standard shape) will not promisify correctly without a custom wrapper.
  • Many of Node's own built-in APIs already ship a .promises variant (e.g. fs.promises) or accept util.promisify directly (util.promisify(fs.readFile)) — a good answer knows both paths exist and that the built-in .promises versions are generally preferred where available, since they are maintained directly rather than wrapped.
  • This directly enables using async/await with a legacy callback API without waiting for (or forking) that library to add native Promise support — the wrapping happens once, typically in a small compatibility module, not at every call site.
  • A precise answer distinguishes util.promisify (a general, one-function-at-a-time conversion utility) from util.callbackify (its inverse — wrapping an async function to expose an error-first callback API instead), which solves the opposite integration problem.

Clarifying questions expected:

  • "Does the callback-style function actually follow the standard error-first, callback-last convention?" — util.promisify assumes exactly that shape.
  • "Is there already a built-in .promises variant available for this specific API?" — often preferable to wrapping manually.

Code / implementation expected: Yes — demonstrating both the success path and a genuine rejection path from a promisified function is the concrete, convincing part of this answer.

utilitiespromisesasync
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js phone screens — assumes basic Promise and callback familiarity. Difficulty: Easy

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. Both the success and rejection paths below were **actua

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

util.promisify verified for both the success path and a genuine rejection path
const util = require("util");

function legacyAdd(a, b, cb) { setTimeout(() => cb(null, a + b), 10); }
const addAsync = util.promisify(legacyAdd);
addAsync(2, 3).then((r) => console.log("promisified result:", r));
// promisified result: 5

function legacyFail(cb) { setTimeout(() => cb(new Error("legacy failure")), 10); }
util.promisify(legacyFail)().catch((e) => console.log("promisified rejection:", e.message));
// promisified rejection: legacy failure

// A method needing "this" binding before promisifying:
// const readAsync = util.promisify(client.legacyRead.bind(client));
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 21 of 152 decoded in the Node.js track. One more won't hurt.

Back to track