In the world of Node.js, many APIs and libraries were built before the introduction of promises. As a result, they rely on a callback-based solution. However, working with nested callbacks can lead to a complex and messy code structure commonly known as “callback hell.” Thankfully, there is a solution: the use of promises and the await keyword.
To remove callbacks and make use of promises, Node.js provides a useful utility called promisify
from the util
module. By utilizing promisify
, you can convert callback-based functions into functions that support promises and enable the use of the async/await syntax.
To use promisify
, import it from the util
module:
const { promisify } = require('util');
Then, create new functions using promisify
:
const ahget = promisify(client.hget).bind(client);
const asmembers = promisify(client.smembers).bind(client);
const ahkeys = promisify(client.hkeys).bind(client);
By adding the letter “a” as a prefix to the function name, it indicates that the function is now asynchronous.
With these promisified functions, you can transform a callback-based “callback hell” scenario into a cleaner and more readable code structure by using the async/await syntax:
const currentUserName = await ahget(`user:${req.session.userid}`, 'username');
const followers = await asmembers(`followers:${currentUserName}`);
const users = await ahkeys('users');
res.render('dashboard', {
users: users.filter((user) => user !== currentUserName && followers.indexOf(user) === -1)
});
This approach is particularly useful when working with third-party libraries where you don’t have direct access to modify the original callback-based functions.
Under the hood, the promisify
function wraps the original function in a promise, allowing you to use await to handle the asynchronous behavior.
Alternatively, you can manually create a promise and return it from a function, allowing you to use async/await syntax:
const handleLogin = (req, user) => {
return new Promise((resolve, reject) => {
req.login(user, (err) => {
if (err) {
return reject({
error: true,
message: err,
});
}
return resolve({
success: true,
});
});
});
}
// ...
const resultLogin = await handleLogin(req, user);
By using promises and await, you can simplify the handling of callback-based functions and create more readable and maintainable code.