Adapting Rusty Old Callbacks to Shiny Async Functions
Tags: showdev, javascript, async, codereview
Published: 2017-12-19 16:19:47

---
cover_image: https://source.unsplash.com/lBrx8YxEMsY
title: Adapting Rusty Old Callbacks to Shiny Async Functions
published: true
description: Transform functions with callbacks into Promise-based, async-ready functions using asyncAdapter.
tags: showdev,javascript,async,codereview
---

While writing code for a side project I stumbled upon some use cases for the recent `async`/`await` feature in Javascript/Node.js, which led me to write [this article](https://dev.to/geoffdavis/writing-asyncawait-middleware-in-express-6i0).

Continuing on in that project, I realized that the library I was using to interact with my database was using callback functions; this isn't the worst pattern in the world, but I was writing a wrapper class around the API and found it clunky sending the results of a query from the deeply-nested callbacks. Ultimately, it works fine, but I wanted a more elegant solution that is easier to follow.

I then got to thinking "what if there was a way that I could wrap some callback-utilizing functions in a way that would let me use asynchronously while also keeping my code DRY". 

And from that spark, [**`asyncAdapter`**](https://github.com/geoffdavis92/asyncAdapter) was born.

## The Problem

Let's say your has a function that creates an `XMLHttpRequest` which passes response data to a callback function, for example:

```javascript
function httpGET(endpoint, callback) {
  var xhr = new XMLHttpRequest();
  xhr.addEventListener("readystatechange", function() {
    if (this.readyState == 4 && this.status == 200) {
      callback(JSON.parse(xhr.responseText));
    }
  });
  xhr.open("GET", endpoint);
  xhr.send();
}
```

It has been a trusty sidekick, but it is a little outdated and makes using retrieved data more involved than modern Javascript needs to be.

You want to use the latest and greatest APIs that tc39 and Babel can provide– like `async/await` or the `Promise` API– and callbacks just don't cut it.

What could you do?

## My Solution

Enter **`asyncAdapter`**. This nifty utility **_magically_** makes the function into a new `Promise`-based function, allowing it to be `await`-ed or otherwise handled like a Promise; this is achieved by passing in the Promise's `resolve` argument where the original function's callback would go.

(Okay so it is not exactly _magic_, but it's still pretty cool)

Here is how you would use the example function above with `asyncAdapter`:

```javascript
const asyncHttpGET = asyncAdapter(httpGET, "https://example.com/api/data");

(async function someAsyncFunction() {
  const data = await asyncHttpGET;
  console.log(data); // -> { foo: 'bar' }
})();
```

The **first argument** to the adapter is the original function name, and the **rest of the arguments** constitute any arguments you would pass to the original function, in the same order.

Note that you should **not** pass a function in the callback parameter's position into the `asyncAdapter` arguments, unless that function can return a value (e.g. not for an AJAX/`Promise`-based function).

Here is an example of a non-asynchronous function being used with `asyncAdapter`:

```javascript
// Original function
const add = (n1, n2, callback) => callback(n1 + n2);

// Add callback function to return value
const asyncSum20 = asyncAdapter(add, 10, 10, n => n);

// Add callback function to return value with side effects
const asyncSum50 = asyncAdapter(add, 10, 10, n => n + 30);

// Use inside function to create DRY async version of original function
const asyncSum = (n1, n2, n3) => asyncAdapter(add, n1, n2, n => n + n3);

(async function someAsyncFunction() {
  const sum20 = await asyncSum20;
  const sum50 = await asyncSum50;
  const sum100 = await asyncSum(5, 20, 75);

  console.log(sum20); // -> 20
  console.log(sum50); // -> 50
  console.log(sum100); // -> 100
});
```

I have found that this implementation is fairly flexible and provides some benefits of functional programming when wrapping the adapter in a function (like the `asyncSum` function above).

Do note that this may not work 100% out of the box for every third-party callback-based function; because `asyncAdapter` depends on the callback argument being the last set in the function parameters, this pattern may end up being more valuable to those who can apply it to their own codebase's functions and have control over those functions' parameter order.

## Conclusion

Does this sound like something you could use? Or perhaps a fun utility function to play around with? 

You're in luck! I just published this to the npm registry [here](https://www.npmjs.com/package/async-adapter).

Install it with your favorite npm client...

`npm i async-adapter`

`yarn add async-adapter`

Find an bug or have an idea for a feature? File an [issue](https://github.com/geoffdavis92/asyncAdapter/issues) or submit a [pull request](https://github.com/geoffdavis92/asyncAdapter/pulls).


I hope you enjoy the package and find it useful. Thanks for reading!

### Further Reading

* [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)
* [await keyword](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await)