From Error-First Callback Functions to Promises in JavaScript

Make the transition from error-first callback functions to promises in JavaScript with our guide. Learn how to use promises for better error handling and a smoother development process.

#javascript
#nodejs
#promises
From Error-First Callback Functions to Promises in JavaScript
Picture by timJ on Unsplash

The Problem

Asynchronicity is a core concept in JavaScript. We know different techniques to handle asynchronous functions, which occur in many operations of our program, especially when we need to handle AJAX requests.

Let’s recap some of them.

Error-first callback

The error-first pattern consists of executing a function when the asynchronous operation ends (such as an incoming AJAX response) which takes as first argument an error, if one occurred, and the result of the request as extra arguments.

Here is an example using .readFile from the file system module of Node.js:

js
fs.readFile('./path/to/file.txt', (err, data) => {
if (err) throw err;
console.log(data);
});

The main problem? The callback hell! If we start nesting asynchronous operations, our code can become very messy.

Promises

Introduced in ES6, this API helps developers to better handle the async actions.

One powerful feature of promises is that we can chain asynchronous operations. The article is not focused on how promises work in-depth, but we can understand it with a simple example:

js
// The fetch API makes an AJAX request and returns a promise
fetch('http://myapp.com/')
.then(response => {
console.log(response);
})
.catch(error => {
console.error(error);
});

Async/await

Added in ES7, this is a wrapper around promises, working with async functions and making our code execution look like synchronous execution:

js
async function getData() {
try {
const response = await fetch('http://myapp.com/');
const body = await response.json();
console.log(body);
} catch (error) {
console.error(error);
}
}

Working with promises makes our code cleaner and more readable but, unfortunately, some APIs still work only with the error-first callback pattern.

How can we convert them to work with promises?

The Solution

There are plenty of utilities and ready-to-use libraries online to promisify a function. But, for learning, I prefer to write some code that can be useful in the future if you face an API with no integrated support for promises.

Let’s write a possible solution:

js
function promisify(func) {
return function promisified(...args) {
return new Promise((resolve, reject) => {
func(...args, (error, data) => {
if (error) reject(error);
else resolve(data);
});
});
};
}

const readFilePromise = promisify(fs.readFile);

readFilePromise('./path/to/file.txt', 'utf8')
.then(data => console.log(data))
.catch(error => console.error(error));

Let’s comment what’s going on:

  • The utility creates a closure, returning a new function that will be our promisified version.
  • The promisified function, when invoked, returns a new promise.
  • Inside the promise constructor callback, we invoke the asynchronous function which we are converting to work with promises and we handle the async action with the error-first callback.
  • If an error occurs, it will be rejected.
  • Otherwise, the promise will resolve the result.

Last updated: