Seq is a node.js library for
chainable, asynchronous flow control. With Seq, you can turn complicated nested
callback logic into a cleaner and more straightforward pipeline-style. Even the
error handling is chainable! Plus, you can do fancy things like set limits on
the number of parallel tasks executing at once.

Here's a simple example of Seq that executes some shell commands and reads
files.

In each .seq() and .par(),
use this in place of a callback and the next action
down the chain waiting on the result will fire when the result becomes available.

.seq() waits for all running actions to stop before
itself firing and the next action in the chain doesn't fire until
.seq() has yielded its result.

.par() executes immediately and moves along to the
next action in the chain. .par()'s result is pushed
onto the argument stack in the order it appeared in the chain, so you can
chain together parallel actions and .seq() to join the
results:

Here, despite 'c' finishing first, its result shows up last in the final call to
.seq() because it was last in the chain of
.par()s.

Errors are propagated through the first argument to
this().
When an error occurs (a non-falsy value is given),
it travels down the chain to the first
.catch() it sees.
If there hasn't been an error, .catch()
just gets skipped. By default there is a
.catch()
at the end of all chains for uncaught errors that looks like:

.catch(function (err) {
console.error(err.stack ? err.stack : err)
})

This approach gets rid of a lot of the duplication and hassle of error handling
since most of the time when an error occurs you just want to print it and abort
the transaction. You can throw in custom logic if you need it at the same time,
so I figure this is a pretty reasonable default.

Seq borrows heavily from other flow control libraries like
Step and
Async.js
but provides a chainable interface instead of array-based actions.
This chainable approach means you don't need to chain multiple functions
together in the same call, but more importantly gives fancier parallel flow
control a more consistent interface. For instance:

In the above example, the first .seq() pulls down the
directory list asynchronously from __dirname, flattens
the results so they fill out the argument stack instead of the first argument,
and computes the size of each of the files in parallel with
.parEach().

.parEach() takes an optional concurrency limit as its
first argument, so to make that previous example only have at most 2
asynchronous fs.stat() operations waiting at a time,
just change: