[JS] A well Brushed Reusable Async Flow

mrbar42
4 min readDec 30, 2016

Or “Async + Reusable = Profit”

Its been a while now since the hype of the dying “callback hell” and the bright Promise that came to rescue us from that hell. now we have generators (ES6) and await/async (ES7)… yay! even more options to choose from.

Did all these features really solve anythings? I tried using promises/generators for a while but it was clear that it doesn’t solve anything — just another way of doing things. Truth be told not only did it make me write more code, but it also didn’t make me feel any better about my async code. does this look familiar?

As I examined my options for handling async flow, I ended up selecting promises after all, because they seemed like there is some potential to them with their synchronous-like syntax (return/throw) and their baked in error handling methodology. they can easily be supported in any node version as well as any modern browser and most importantly they blur the line between sync and async actions seamlessly.

Since performance is always an issue as well as stability I decided to use bluebird since it performs better than the native implementation. that being said, I won’t be using any special features other than the basic spec of promises. I find these features confusing and not enough beneficial for their learning curve and hidden complexity when reading the code.
For clarity, when I say the basic promise spec I mean:

new Promise()
Promise.resolve()
Promise.reject()
promise.then()
promise.catch()
promise.all()
promise.race()

Reusability is important… if you don’t agree you can happily continue writing parseQuery loadUser validateParams and more of these ever-needed functions and so on until the time-continuum melts into space.

The key to reusability is defining a standard way of doing an action. a good example of this would be npm. we all know how it works…
npm i -S package and then require('package').

A standard way of doing things is more powerful than 1000 features

Flow function

My standard way is built upon two types of functions, flow functions and payload functions.
Flow function doesn’t performs any direct action, but rather they orchestrates a chain of actions to achieve a larger goal. To keep things simple lets assume we are writing a user authentication api — its an oversimplified example, but it gets the job done.

Since payload functions must be dumb, we need an easy standard way to communicate with them. for that I will use a payload object that will be passed throughout the chain.
example flow function:

Payload function

A payload function is the dumbest as it can get. it should perform a very specific action and nothing more, no side effects and no detached async operations. a payload function can be either synchronous or asynchronous or both! since promises can accept both a value AND another promise, that means you can dynamically decide if the action is sync or async.
Payload function must obey these three simple rules
1. all data is either consumed from the payload or added to it
2. eventual value must be the payload
3. must have inline docs

payload functions are the basic reusable blocks. here is an example:

Note the documentation of the function, its very clear what are the requirements of the function from the payload, what does it add to the payload and what kind of errors it may throw.

Lets use a case

Lets create a basic index file that passes requests to the api module:

and an api module with route handlers skeletons:

Now lets try to implement the base flow function.

Notice that i use some inline functions within the flow function, i will explain latter on what’s the point of doing that.
I also used my first payload function respondFromPayload:

I care much about security so I am going to whitelist and validate the params. how? you guessed it… another payload function

its a synchronous function? yes… but who cares? promises treat sync and async return values with the same manner.

next step, loading user and checking parameters against database… once again a reusable payload functions

create session token:

now lets update our flow function to include all payload functions and possible errors

since the payload functions uses a payload object for both input and output any function can be added in between any of them as long as it obeys the payload function rules.
for the sake of example lets add a log after loading the user from db.

Each of the payload functions can be easily reused for other purposes because its very clear what its action is.

You might have wondered why I used an inline function for the catch method in the flow function. the easy answer is that catch function gets the error as its parameters so there is no direct access to the payload, closure is used to solve that.
There are more solutions to this such as pre-capturing payload reference like so:

If this method would have been used in the example, the flow function would have gotten even shorter:

Bottom Line

Look up, this last example is basically how my code looks nowadays. a general purpose payload functions that do various yet simple actions, and a function the composes a chain of actions to achieve a required flow.

In the examples a simple api is used to demonstrate the agenda, but this way of doing things applies to anything you may think of. You are forced to write generic code and use the payload as your sandboxed link.

I finally found an async flow that I’m satisfied with, maybe you’ll find it suitable for you as well. share your thoughts.

--

--