tezvyn:

How do you wrap a callback-based API into a Promise?

AI-drafted, machine-checkedSource: developer.mozilla.orgbeginner
How do you wrap a callback-based API into a Promise?

This tests Promise constructor mechanics and callback migration. A strong answer returns a new Promise, calls the legacy function, maps success to resolve and errors to reject.

WHAT THIS TESTS: Your understanding of the Promise constructor as an interoperability layer between legacy Node-style or browser callback APIs and modern async code. The interviewer wants to see that you know promises are created with new Promise, that you manually control resolution and rejection, and that you understand the difference between a function that takes a callback and a function that returns a Promise.

A GOOD ANSWER COVERS: First, return a new Promise from the wrapper function so callers can use then and catch. Second, inside the executor function, invoke the original legacy function with the provided id. Third, pass a callback that checks for an error argument; if present, call reject with that error, otherwise call resolve with the result. Fourth, ensure you do not return the result of the legacy call, because callback-based functions often return undefined, and the wrapper must return the Promise itself.

COMMON WRONG ANSWERS: A major red flag is forgetting to handle the error branch and only calling resolve, which causes errors to silently disappear. Another is calling resolve with the error instead of reject, which makes failures look like successes. Some candidates try to use async and await inside the wrapper without returning a new Promise, or they return the legacy function's undefined return value instead of the Promise object. A subtle mistake is assuming the callback signature is always node-style error-first; in the browser it might be success-first, so a senior candidate should ask or check the callback convention.

LIKELY FOLLOW-UPS: The interviewer might ask how to promisify an entire module or API surface, which leads to util.promisify in Node or a manual wrapper factory. They might ask what happens if the legacy function throws synchronously, which should be caught and passed to reject. They could also ask about TypeScript typing, specifically how to type the wrapper so it infers the resolve type from the callback and uses unknown or Error for the reject type.

ONE CONCRETE EXAMPLE: Imagine a legacy function getUser(id, callback) where callback is function(err, user). The wrapper is function getUserAsync(id) { return new Promise((resolve, reject) => { getUser(id, (err, user) => { if (err) { reject(err); return; } resolve(user); }); }); }. This returns a Promise, maps the error-first callback correctly, and does not leak the undefined return of getUser.

Source: developer.mozilla.org

Read the original → developer.mozilla.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.