How to use promises and await with Node.js callback-based functions
Most of the Node.js APIs were built in a time where promises weren’t a thing yet, and they use a callback-based solution.
The typical Node.js API works like this:
doSomething(param, (err, result) => {
})
This also applies to libraries. One example is node-redis, and while working with it on a project, at some point I really had the need to remove all the callbacks, because I had too many levels of callbacks nested into each other - a perfect “callback hell” scenario.
Also, sometimes it’s absolutely necessary to avoid callbacks because you need to return from the function the result of a function call. If that’s returned in a callback, the only way to get the result back would be to send it back with a function, and the callback party continues:
const myFunction = () => {
doSomething(param, (err, result) => {
return result //can't return this from `myFunction`
})
}
const myFunction = callback => {
doSomething(param, (err, result) => {
callback(result) //no
})
}
myFunction(result => {
console.log(result)
})
There’s an easy solution.
A solution provided by Node.js itself.
We can “promisify” any function that does not support promises (and as a consequence the async/await syntax) by importing promisify from the core Node.js util module:
const { promisify } = require('util')
Then we create new functions using it:
const ahget = promisify(client.hget).bind(client)
const asmembers = promisify(client.smembers).bind(client)
const ahkeys = promisify(client.hkeys).bind(client)
See how I added the a letter to mean async.
Now we can change this example “callback hell”:
client.hget(`user:${req.session.userid}`, 'username', (err, currentUserName) => {
client.smembers(`followers:${currentUserName}`, (err, followers) => {
client.hkeys('users', (err, users) => {
res.render('dashboard', {
users: users.filter((user) => user !== currentUserName && followers.indexOf(user) === -1)
})
})
})
})
into a much cleaner:
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 is optimal when using a function you don’t have access to, like in this case where I use a 3rd party library.
Under the hood, promisify wraps the function in a promise, and returns it.
You can do this manually, too, returning a promise from a function, and then using it with async/await:
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) download all my books for free
- javascript handbook
- typescript handbook
- css handbook
- node.js handbook
- astro handbook
- html handbook
- next.js pages router handbook
- alpine.js handbook
- htmx handbook
- react handbook
- sql handbook
- git cheat sheet
- laravel handbook
- express handbook
- swift handbook
- go handbook
- php handbook
- python handbook
- cli handbook
- c handbook
subscribe to my newsletter to get them
Terms: by subscribing to the newsletter you agree the following terms and conditions and privacy policy. The aim of the newsletter is to keep you up to date about new tutorials, new book releases or courses organized by Flavio. If you wish to unsubscribe from the newsletter, you can click the unsubscribe link that's present at the bottom of each email, anytime. I will not communicate/spread/publish or otherwise give away your address. Your email address is the only personal information collected, and it's only collected for the primary purpose of keeping you informed through the newsletter. It's stored in a secure server based in the EU. You can contact Flavio by emailing [email protected]. These terms and conditions are governed by the laws in force in Italy and you unconditionally submit to the jurisdiction of the courts of Italy.