Callback-Hell
Installation
npm install callback-hell --save
Prerequisite Knowledge
the callback-hell library allows for async functions to be easily chained together.
The library expects all functions to expect callbacks which themselves expect input in a specific format:
Wrapped Result
a wrapped result is a simple object, that can contain:
- a result (a non-null value, mapped from key:
result
) - an error (a non-null value, mapped from key:
error
)
If a non-null error is present, the wrapped result is always treated as an error value - despite also potentially having a non-null result value.
Fixing non-compliant functions
Common callback signatures are:
var { };var { };var { };
Using wrapper methods provided by the library, we can fix functions that expect the above callbacks as follows:
var h = ; var { ; };var { ; }; //ew - stands for ErrorWrap var { ; };var { ; }; //rw - stands for ResultWrap var result = null;var { ; };var { ; }; //bw - stands for Both (Error and Result) Wrap
Write Orders
a write order is a simple object. It contains:
- a value ( mapped from key
value
) - a key ( mapped from key
key
)
Using wrapper methods similar to above, we can extend a callback cb
, such that any wrapped result value being passed in is first itself wrapped in a write order:
var result = null;var { ; }; // in two steps of wrapping: first turning it compliant -> then decorating with a write order:var { ; }; //bw - stands for Both (Error and Result) Wrapvar { ; }; // or all at once (notice the ordering seems to be reversed):var { ; };
These write orders allow us to cleanly access previous computations when chaining our async functions.
Async Chaining Functions
AsyncSerial
This function calls a list of functions in sequence, one after another. It expects two arguments:
- a list of functions
- a final callback to be run
The functions in the list must be of type:
var { };
The reader
argument lets us examine the results of previously run async functions in the list that have used write orders to 'save' their results.
This is done by calling the get
method on the reader object, which takes a key as an argument.
The final async is passed an object containing all of the write orders that have been executed in the list.
If any of the functions return an error value (in a wrapped result of course), no further async functions in the list are run. The error value is immediately passed to the final callback instead of a dump of the write orders.
AsyncParallel
This function calls a list of functions all at once. It expects two arguments:
- a list of functions
- a final callback to be run
The functions in the list must be of type:
var { };
Unlike the serial function, we don't expect a reader - as it doesn't make sense in a parallel context.
The final callback is called immediately if any of the functions error - it is passed the error value.
Alternatively, upon successful completeion of all functions, a dump of the write orders is instead passed to the final callback (like in AsyncSerial
Utils
Also provided are a collection of utilities:
Additional wrapper functions exist which aid in modifying results before they are passed to the wrapped callback:
// h.mw (or MapWrap), allows us to modify the wrapped result (if present, i.e. if not an error), by using// a specified 'mapping' function:var { ; }; // h.ix (or IndexWrap), is a common case of map wrap. It is used to extract a value from an object using a specified keyvar { ; };
Also, functions exist which let you examine the status of a wrapped result:
var err = h;h; //evaluates to true
Code Examples
Toy database library:
var db // function(err,res) { ... };var db // function(err) { ... };
Account Exists -> Parallel Insert
Code that checks the existence of an account - and based on its existence, adds some entries to the db
var h = ;var _ = ; var accountId = 'foo';var valsToAdd = 123456; // create parallel async funcs for the insert - we don't care what order they're run. var insertFns = _; var mainFns = // first lift the value into a wrapped result -> then -> index the value using the key 'num' -> then -> wrap the value in a write order with the key 'num' { db; } { if re === 0
Account Exists -> Parallel Get
Code that checks the existence of an account - and based on its existence retrieves some entries from the db
var h = ;var _ = ; var accountId = 'foo';var keys = 123456;var { return x0val; }; // create parallel async funcs for the insert - we don't care what order they're run. var getFns = _; var mainFns = { db; } { if re === 0