How to rewrite a callback function in Promise form and async/await form in JavaScript
Tags: node, javascript
Published: 2019-01-16 02:32:40

---
title: How to rewrite a callback function in Promise form and async/await form in JavaScript
published: true
canonical_url: https://www.coreycleary.me/how-to-rewrite-a-callback-function-in-promise-form-and-async-await-form-in-javascript/
tags: nodejs,javascript
---
<em><strong>Originally published at <a href="https://www.coreycleary.me/how-to-rewrite-a-callback-function-in-promise-form-and-async-await-form-in-javascript/">coreycleary.me</a></strong>. This is a cross-post from my content blog. I publish new content every week or two, and you can <a href="https://www.coreycleary.me/about/">sign up to my newsletter</a> if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies!</em>

<blockquote>"You should really use Promises or async/await here to make this more readable"</blockquote>

How many times have you posted some code snippet when trying to get an answer to your question, and someone ends up pestering you about this? Now, on top of whatever problem you already have with your code, you have another thing you need to learn and "fix"...

Or what about dealing with refactoring an existing, callback-based codebase at work? How do you convert them to native JavaScript Promises? It would be so great to be able to develop using modern JavaScript and start making use of the `async/await` functionality...

If you knew how to avoid callbacks, you could post your code online when asking for help without people asking you to rewrite it and not actually answering your question.

And if you were refactoring an existing codebase, that code would be more readable, you could avoid the "callback hell" people still seem to talk about even in 2019 when Promises have had support in many browsers and Node for years now, and `async/await` is supported by many versions as well...

<h3>The fix</h3>

Let's go over how to convert those old-school callbacks to Promises and to `async/await` versions.

<a href="https://github.com/coreyc/converting-callbacks" rel="noopener" target="_blank">Here's the link to the code</a> demonstrating the callback -> Promise and callback -> `async/await` versions.

<h3>Callback version</h3>

```javascript
const callbackFn = (firstName, callback) => {
  setTimeout(() => {
    if (!firstName) return callback(new Error('no first name passed in!'))
    
    const fullName = `${firstName} Doe`
    
    return callback(fullName)
  }, 2000)
}

callbackFn('John', console.log)
callbackFn(null, console.log)
```

You'll notice here that we're using the `setTimeout()` function in order to make our function asynchronous. In addition to `setTimeout()`, other asynchronous operations you're likely to see in the real-world are: AJAX and HTTP calls, database calls, filesystem calls (in the case of Node, if no synchronous version exists), etc.

In this function, we "reject" it if the first name argument is null. When we do pass in the `firstName` argument, the callback function (almost always the last argument in a callback-based function's argument list) gets called and returns our value after the 2 seconds set in `setTimeout()`.
<img src="https://www.coreycleary.me/wp-content/uploads/2019/01/callback-explanation-2.png" alt="" width="645" height="208" class="aligncenter size-full wp-image-898" />

If we don't pass in a callback, we get a `TypeError: callback is not a function` error.

<h3>Promise version</h3>
And here's the Promise-based version of that function:
```javascript
const promiseFn = firstName => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (!firstName) reject(new Error('no first name passed in!'))
      
      const fullName = `${firstName} Doe`  
      
      resolve(fullName)
    }, 2000)
  })
}

promiseFn('Jane').then(console.log)
promiseFn().catch(console.log)
```
Converting to a Promise-based function is actually pretty simple. Look at the below diagram for a visual explanation:
<img src="https://www.coreycleary.me/wp-content/uploads/2019/01/callback-to-promise-1.png" alt="" width="619" height="454" class="aligncenter size-full wp-image-901" />
First, we remove the callback argument. Then we add the code to return a `new Promise` from our Promise-based function. The error callback becomes a `reject`, while the "happy path" callback becomes a `resolve`.

When we call the `promiseFn`, the result from the happy path will show up in the `.then()`, while the error scenario will show up in the `.catch()`.

The great thing about having our function in Promise form is that we don't actually need to "make it an async/await version" if we don't want to. When we call/execute the function, we can simply use the `async/await` keyword, like so:
```javascript
const result = (async () => {
  try {
    console.log(await promiseFn('Jim')) 
  } catch (e) {
    console.log(e)
  }

  try {
    console.log(await promiseFn()) 
  } catch (e) {
    console.log(e)
  }
})()
```
<em>Side note: here I wrapped the function call in an IIFE - that's what that `(async () => {....})()` is if you've never seen it. This is simply because we need to wrap the `await` call in a function that uses the `async` keyword, and we also want to "immediately invoke" the function (IIFE = "Immediately Invoked Function Execution") in order to call it.</em>

Here, there are no callbacks, no `.then()`'s or `.catch()`'s, we just use a `try/catch` block and call the `promiseFn()`. Promise rejections will be caught by the `catch` block.

<em>Note: `async/await` is available in most <a href="https://caniuse.com/#feat=async-functions" rel="noopener" target="_blank">semi-recent releases of the major browsers</a>, with the exception of Internet Explorer. Node has had support for the feature since version 7.6.0</em>

<h3>async/await version</h3>
But what if we wanted to convert a callback function directly to an `async/await` version of that function? Without using Promises directly?

`async/await` is syntactic sugar around Promises, so it uses them under the hood. Here's how you can convert it:
```javascript
const timeout = ms => {
  return new Promise(resolve => setTimeout(resolve, ms))
}

const asyncAwaitFn = async firstName => {
  await timeout(2000) // using timeout like this makes it easier to demonstrate callback -> async/await conversion
  
  if (!firstName) throw new Error('no first name passed in!')
    
  const fullName = `${firstName} Doe`
    
  return fullName
}

const res = (async () => {
  try {
    console.log(await asyncAwaitFn('Jack')) 
  } catch (e) {
    console.log(e)
  }

  try {
    console.log(await asyncAwaitFn()) 
  } catch (e) {
    console.log(e)
  }
})()
```
Use the below diagram to understand how to go from callback to `async`:

<img src="https://www.coreycleary.me/wp-content/uploads/2019/01/callback-to-async-1.png" alt="" width="624" height="427" class="aligncenter size-full wp-image-902" />
Similar to converting to the Promise-based version, we get rid of the callback passed in to the original function, as well as that argument call within the body of the function. Next, we add the `async` keyword to the beginning of the function declaration. And finally, when we hit the error scenario, we throw an `Error`, which results in a rejected Promise (caught in the `catch` block when we call the function), and simply return the `fullName` in the happy path scenario.

Note that `async` functions all return Promises, so when you use `return` you are just resolving the Promise.
<h3>Wrapping up</h3>
Next time you need to convert a callback-based function to a Promise-based one or an `async/await`-based versions, use the visual diagrams from this post to quickly and easily do so. And if you need some code to play around with to help the concepts settle some more, <a href="https://github.com/coreyc/converting-callbacks" rel="noopener" target="_blank">here's the link again to the code</a> demonstrating the callback -> Promise, and callback -> `async/await` versions.

Callback hell is now gone!

I have plenty more content planned for the future, so if you found this helpful and want to receive it directly to your inbox without having to remember to check back here, <a href="https://www.coreycleary.me/about/">here's the link again for the newsletter!</a>