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 my free Node.js Handbook
More node tutorials:
- An introduction to the npm package manager
- Introduction to Node.js
- HTTP requests using Axios
- Where to host a Node.js app
- Interact with the Google Analytics API using Node.js
- The npx Node Package Runner
- The package.json guide
- Where does npm install the packages?
- How to update Node.js
- How to use or execute a package installed using npm
- The package-lock.json file
- Semantic Versioning using npm
- Should you commit the node_modules folder to Git?
- Update all the Node dependencies to their latest version
- Parsing JSON with Node.js
- Find the installed version of an npm package
- Node.js Streams
- Install an older version of an npm package
- Get the current folder in Node
- How to log an object in Node
- Expose functionality from a Node file using exports
- Differences between Node and the Browser
- Make an HTTP POST request using Node
- Get HTTP request body data using Node
- Node Buffers
- A brief history of Node.js
- How to install Node.js
- How much JavaScript do you need to know to use Node?
- How to use the Node.js REPL
- Node, accept arguments from the command line
- Output to the command line using Node
- Accept input from the command line in Node
- Uninstalling npm packages with `npm uninstall`
- npm global or local packages
- npm dependencies and devDependencies
- The Node.js Event Loop
- Understanding process.nextTick()
- Understanding setImmediate()
- The Node Event emitter
- Build an HTTP Server
- Making HTTP requests with Node
- The Node fs module
- HTTP requests in Node using Axios
- Reading files with Node
- Node File Paths
- Writing files with Node
- Node file stats
- Working with file descriptors in Node
- Working with folders in Node
- The Node path module
- The Node http module
- Using WebSockets with Node.js
- The basics of working with MySQL and Node
- Error handling in Node.js
- The Pug Guide
- How to read environment variables from Node.js
- How to exit from a Node.js program
- The Node os module
- The Node events module
- Node, the difference between development and production
- How to check if a file exists in Node.js
- How to create an empty file in Node.js
- How to remove a file with Node.js
- How to get the last updated date of a file using Node.js
- How to determine if a date is today in JavaScript
- How to write a JSON object to file in Node.js
- Why should you use Node.js in your next project?
- Run a web server from any folder
- How to use MongoDB with Node.js
- Use the Chrome DevTools to debug a Node.js app
- What is pnpm?
- The Node.js Runtime v8 options list
- How to fix the "Missing write access" error when using npm
- How to enable ES Modules in Node.js
- How to spawn a child process with Node.js
- How to get both parsed body and raw body in Express
- How to handle file uploads in Node.js
- What are peer dependencies in a Node module?
- How to write a CSV file with Node.js
- How to read a CSV file with Node.js
- The Node Core Modules
- Incrementing multiple folders numbers at once using Node.js
- How to print a canvas to a data URL
- How to create and save an image with Node.js and Canvas
- How to download an image using Node.js
- How to mass rename files in Node.js
- How to get the names of all the files in a folder in Node
- How to use promises and await with Node.js callback-based functions
- How to test an npm package locally
- How to check the current Node.js version at runtime
- How to use Sequelize to interact with PostgreSQL
- Serve an HTML page using Node.js
- How to solve the `util.pump is not a function` error in Node.js