# Async Good

In praise of `async` by [@bwasti](https://twitter.com/bwasti)


Not to brag, by my function is called *all* the time.
It's called by many other functions.  Here it is:

function my_function(x) {
  return x * 2;

I don't know who is calling it.
I don't know when it's getting called.
It just gets called.

Fun fact about my function: it doesn't have any state.
I tell everyone this.
It's a particularly fun fact.
Other functions know that my function doesn't have any state.
Sometimes they call my function in parallel.
It doesn't matter to me.

### The Feature

Then, one day, my programmer needs to add a feature.
My function isn't doing enough on its own.
It needed to call another function.
On another machine.

That machine is really far away! Nanoseconds
or maybe even *microseconds* away! 😨 My poor function.

But my programmer is smart.  My programmer can deal
with a bit of delay.

function my_function(x) {
  let handle = remote.call('f', x)
  while (!handle.valid) {
  return handle.data
My function now has a while loop that spins.
Not great but not terrible.  It only spins
a couple million times before a result.
At least there's no state!

### The Parallel Callers

Most of my function's callers are happy.
They don't mind the small delay.
But some of my function's callers have complaints.
The parallel callers.

If they call my function three times in parallel,
it takes three times as long.
They didn't notice this originally. But, with the new
delay, they take issue.  Shoot.

Again my programmer is smart.
My programmer negotiates a new API.
Instead of data my function now returns a wrapper.
It is the caller's job to get the data now.

class HandleWrapper {
  constructor(h) { this.handle = h }
  get data() {
    while (!this.handle.valid) {
    return this.handle.data

function my_function(x) {
  let handle = remote.call('f', x)
  return new HandleWrapper(handle)
My function now seems a lot faster.
It isn't actually faster.  But it seems that way.
Callers are happy.

### The Anti-Wrappers

But not for long.
Soon the callers catch on to my programmer's trick.
The wrapper is forcing them to use an unfamiliar
object (`HandlerWrapper`) in an unfamiliar way.

Managers see any change to my function as low impact
at this point.  My function does the job and everyone uses it.
Only the callers know my function's
treachery.  A useless type? A fragile API? Misplaced computation?
A waste of resources?
It all falls under the umbrella of tech-debt.
Managers don't care about that.

But then the callers start a movement.
And, more importantly, they give it a name.
The Anti-Wrapper Movement (AWM).

Branding has a suprisingly large impact.
Every caller is now on the same page: wrappers are bad.
Managers can understand this (because it is three words).

My programmer is made to change my function yet again.
This time it takes a while.
The fix requires shared state and a complex
convention: callbacks.

const queue = []

setInterval(function runner() {
  for (let [handle, callback] of queue) {
    if (handle.valid) {
}, 100)

function my_function(x, callback) {
  let handle = remote.call('f', x)
  queue.push([handle, callback])

Instead of returning data, my function now calls the caller's function
and passes data as the input.

function callback(data) {
  // do something with the data

my_function(x, callback)

It's a bit complicated but it solves all the problems elegantly.

The original users are alright with the change.
Parallel callers are happy because things are now more parallel.
Anti-wrappers are happy because there are no fragile types to deal with.

Seems great.

### The Replicants and Callback Hell

Now there is a precedent for callbacks and
my function is called more than ever.
New hires and new projects witness the success of my function.

They see the benefits and replicate the API.
Now most functions use callbacks.
Sometimes the callbacks aren't even necessary.
But that doesn't matter.  Best practice is best practice.

Unfortunately this convention has
a small side effect: it creates hell. Callback hell.

Simple code
function j(a) {
  const b = f(a)
  const c = g(b)
  const d = h(c)
  const e = i(d)
  return e

Has become not so simple...
function j(a, callback) {
  f(a, function (b) { 
    g(b, function (c) {
      h(c, function (d) {
        i(d, function (e) {
Many are sad. 
Readability has disappeared.
On-boarding has tripled in time.
Debugging has become nearly impossible.
Productivity has dried up.

But my function still works.
It can still call into a remote machine
without blocking users.

### Async Heaven

My programmer starts to feel bad for creating a hell.
So my programmer rewrites my function:

async function my_function(x) {
  return await remote.call('f', x)

This is nearly the same as the callback version.

The callers need to change
my_function(x, callback)

But it gets better.
If the callers *also* make their functions `async`, they can do

async function caller(x) {
  const result = await my_function(x)
  // ...

Now callers can use my function the same way they used to!

The original callers will be happy.
The parallel callers will be happy.
The anti-wrapper callers will be happy.

It's perfect.

### The Change!

My programmer commits the change.
No one is happy.  This is the third time
they had to refactor all their code to call my function.


*The end.*