A lot of core APIs in node.js have two forms for each function - synchronous and asynchronous. The synchronous versions block until the function has completed, while the asynchronous versions do not block - instead executing a callback when complete and allowing the calling code to continue in the meantime. This can get messy when you need to chain asynchronous operations together as you have to write callbacks within callbacks, which are difficult to follow.

The Promise specification gets around this by wrapping asynchronous code in a structure called a Promise, and providing methods that allows the chaining together of asynchronous operations in a linear way. Now that Promises are part of the ES6 standard, many new third party APIs are choosing Promises over traditional asynchronous functions.

This is great, but it means there are two kinds of asynchronous functional calls that we may have to deal with. My opinion is that code is more readable when using Promises because it is flat rather than nested, and so when I’ve had to integrate Promise-based APIs with traditional asynchronous APIs, I’ve opted to convert the traditional APIs into Promise-based ones.

There are a few ways to go about this:

  1. Use a library that provides wrapped versions of an api containing traditional async functions e.g. fs-promise for the fs API.
  2. Use a library that provides functions to wrap traditional async functions e.g. Bluebird’s promisify function
  3. Wrap the traditional async functions yourself.

Either of the first two solutions are fine, but it’s worth understanding how to convert a traditional async function to a Promise-based one for situations where those options may not be available, so let’s take a look at the third solution: wrapping the traditional async functions yourself.

We’ll take fs.readFile as an example. The idea is to return a new Promise wrapping the traditional asynchronous function, and invoking the Promise’s resolve or reject functions as necessary in the callback:

const readFileAsPromise = (file, options) =>
  new Promise((resolve, reject) =>
    fs.readFile(file, options, (err, data) =>
      err ? reject(err) : resolve(data)));

This can then be used in a chain of Promises:

readFileAsPromise("/path/to/some/file").
  then(data => console.log(`success: ${data}`)).
  catch(err => console.log(`failure: ${err}`));