Converting callbacks to promises

It’s easier to work with Promises (or Async/await) compared to callbacks. This is especially true when you work in Node-based environments. Unfortunately, most Node APIs are written with callbacks.
Today I want to show you how to convert callbacks to p…


This content originally appeared on Zell Liew and was authored by Zell Liew

It’s easier to work with Promises (or Async/await) compared to callbacks. This is especially true when you work in Node-based environments. Unfortunately, most Node APIs are written with callbacks.

Today I want to show you how to convert callbacks to promises.

Before you read this article, it helps to know what a promise is.

Converting Node-styled callbacks to promises

Callbacks from Node’s API have the same pattern. They’re passed into functions as the final argument. Here’s an example with fs.readFile.

const fs = require('fs') 

fs.readFile(filePath, options, callback)

Also, each callback contains at least two arguments. The first argument must be an error object.

fs.readFile('some-file', (err, data) => {
  if (err) {
    // Handle error 
  } else {
    // Do something with data
  }
})

If you encounter a callback of this pattern, you can convert it into a promise with Node’s util.promisify.

const fs = require('fs')
const util = require('util')

const readFilePromise = util.promisify(fs.readFile)

Once you convert the callback into a promise, you can use it like any other promise.

readFilePromise(filePath, options)
  .then(data => {/* Do something with data */})
  .catch(err => {/* Handle error */}

Once in a while, you may run into APIs that do not conform to Node’s error-first callback format. For these situations, you cannot use util.promisify. You need to write your own promise.

Writing your own promise

To convert a callback into a promise, you need to return a promise.

const readFilePromise = () => {
  return new Promise ((resolve, reject) => {
    // ...  
  })
}

You run the code with the callback inside the promise.

const readFilePromise = () => {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, options, (err, data) => {
      // ...
    })
  })
}

If there’s an error, you reject the promise. This allows users to handle errors in catch.

If there are no errors, you resolve the promise. This allows users to decide what to do next in then.

const readFilePromise = () => {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, options, (err, data) => {
      if (err) return reject(err)
      resolve(data)
    })
  })
}

Next, you need to provide arguments like filePath and options to the code within the promise. To do this, you can use rest and spread operators.

const readFilePromise = (...args) => {
  return new Promise((resolve, reject) => {
    fs.readFile(...args, (err, data) => {
      if (err) return reject(err)
      resolve(data)
    })
  })
}

You can then use readFilePromise as a promise.

readFilePromise(filePath, options)
  .then(data => {/* Do something with data */})
  .catch(err => {/* Handle error */}

Converting non-Node-styled callbacks into promises

Turning a non-Node-style callback into a promise is easy once you know how to construct a promise. You follow the same steps:

  1. Reject if there’s an error
  2. Resolve otherwise

Let’s say you have an API that returns data as the first argument and err as the second argument. Here’s what you do:

const shootPeasPromise = (...args) => {
  return new Promise((resolve, reject) => {
    // This is a not a Node styled callback. 
    // 1. data is the first argument 
    // 2. err is the second argument
    shootPeas(...args, (data, err) => {
      if (err) return reject(err)
      resolve(data)
    })
  })
}

Callbacks with multiple arguments

Let’s say you have a callback with three arguments:

  1. An error object
  2. Some data
  3. Another piece of data
growTrees(options, (error, location, size) => {
  // ... 
})

You cannot write this:

// Note: This does not work 
const growTreesPromise = (...args) => {
  return new Promise((resolve, reject) => {
    growTrees(...args, (error, location, size) => {
      if (err) return reject(err)
      // You can't send two arguments into resolve
      resolve(location, size)
    })
  })
}

The code above doesn’t work because promises can only return one argument. If you want to return many arguments, you can either use an array or an object.

// Using an array object
resolve([location, size])

// Using an object
resolve({location, size})

Then, You can destructure the array or object in the then call.

// If you use arrays
growTreesPromise(options)
  .then([location, size]) => {/* Do something */})

// If you use objects
growTreesPromise(options)
  .then({location, size}) => {/* Do something */})


This content originally appeared on Zell Liew and was authored by Zell Liew


Print Share Comment Cite Upload Translate Updates
APA

Zell Liew | Sciencx (2019-09-18T00:00:00+00:00) Converting callbacks to promises. Retrieved from https://www.scien.cx/2019/09/18/converting-callbacks-to-promises/

MLA
" » Converting callbacks to promises." Zell Liew | Sciencx - Wednesday September 18, 2019, https://www.scien.cx/2019/09/18/converting-callbacks-to-promises/
HARVARD
Zell Liew | Sciencx Wednesday September 18, 2019 » Converting callbacks to promises., viewed ,<https://www.scien.cx/2019/09/18/converting-callbacks-to-promises/>
VANCOUVER
Zell Liew | Sciencx - » Converting callbacks to promises. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2019/09/18/converting-callbacks-to-promises/
CHICAGO
" » Converting callbacks to promises." Zell Liew | Sciencx - Accessed . https://www.scien.cx/2019/09/18/converting-callbacks-to-promises/
IEEE
" » Converting callbacks to promises." Zell Liew | Sciencx [Online]. Available: https://www.scien.cx/2019/09/18/converting-callbacks-to-promises/. [Accessed: ]
rf:citation
» Converting callbacks to promises | Zell Liew | Sciencx | https://www.scien.cx/2019/09/18/converting-callbacks-to-promises/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.