Promisifying Callbacks
Promisifying means converting a callback-based API into a Promise-based API.
This is useful when older code uses callbacks but newer code uses promises or async / await.
Callback-Based Function
function getUser(callback) {
setTimeout(() => {
callback(null, { id: 1, name: "Alice" });
}, 1000);
}Usage:
getUser((error, user) => {
if (error) {
console.log(error.message);
return;
}
console.log(user.name);
});This works, but promise-based code can be easier to compose.
Promise Wrapper
function getUserPromise() {
return new Promise((resolve, reject) => {
getUser((error, user) => {
if (error) {
reject(error);
return;
}
resolve(user);
});
});
}Now you can use:
getUserPromise().then((user) => {
console.log(user.name);
});or:
const user = await getUserPromise();Error-First Callback Pattern
Many Node-style callbacks use this format:
callback(error, data)If error exists, reject.
If there is no error, resolve with the data.
function promisifyRead(operation) {
return new Promise((resolve, reject) => {
operation((error, data) => {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}Manual Promisify Example
function waitWithCallback(ms, callback) {
setTimeout(() => {
callback(null, `Waited ${ms}ms`);
}, ms);
}
function wait(ms) {
return new Promise((resolve, reject) => {
waitWithCallback(ms, (error, result) => {
if (error) {
reject(error);
return;
}
resolve(result);
});
});
}
const message = await wait(1000);
console.log(message);Avoid Promisifying Promise APIs
Do not wrap functions that already return promises.
Avoid:
function loadData() {
return new Promise((resolve, reject) => {
fetch("/api/data")
.then((response) => response.json())
.then(resolve)
.catch(reject);
});
}Better:
function loadData() {
return fetch("/api/data").then((response) => response.json());
}Promisify callback APIs.
Return existing promises directly.
Best Practices
Promisify when working with callback-based APIs.
Reject errors and resolve successful values.
Use Error objects for failures.
Keep wrappers small.
Do not promisify APIs that already return promises.
Common Mistakes
Mistake 1: Forgetting to Return After Reject
if (error) {
reject(error);
}
resolve(data);Only the first settlement matters, but this code is confusing.
Use:
if (error) {
reject(error);
return;
}Mistake 2: Resolving Errors
resolve(error);This fulfills the Promise with an error value.
Use:
reject(error);Mistake 3: Wrapping Existing Promises
If a function already returns a Promise, return it directly.
Summary
Promisifying converts callback APIs into Promise APIs.
- Wrap callback APIs with
new Promise(). - Use
resolvefor success. - Use
rejectfor failure. - Error-first callbacks usually use
(error, data). - Promisified functions can be used with
.then()orawait. - Do not wrap existing Promise-returning functions unnecessarily.