action interceptor for dynamic extensible systems
A means to provide interceptors (i.e. hooks) when performing asynchronous actions.
perform-based actions is asynchronous.
.perform(action ...enable hooking.
By depending on
understudy you are exposed to four methods:
This is the core API for invoking hooks provided by
Understudy. Each call to
perform for the same
action should have a consistent argument signature because this is what will be expected by each of the before and after hooks for the
action. The overall flow control is:
after hooks foraction`.
callbackwith results from
Called before the
work function is executed in perform with exactly the arguments passed to
.perform. Nothing passed to
next have an impact on the flow control above except any error is supplied short-circuits execution to the callback.
Called after the
work function is executed in perform with exactly the
arguments passed to
.perform. Nothing passed to
next have an impact on the
flow control above except any error is supplied short-circuits execution to
While the above statement is true when using
after hooks acquire a
waterfall like behavior with
.waterfall where the result of work function
gets passed to the
after hooks. Each after hook is then able to mutate the
arguments passed to the next one. Strongly discouraged to change number of
arguments for your user's sanity.
This is a slightly different
perform that is very useful for when you have to
modify state received from a function in a sequence of configurable hooks.
after hooks foraction
with the result returned from thework` function.
callbackwith results from the
afterhooks execution (if any) and otherwise the results from the
Let's consider a real-world application with two interceptable actions:
start: Application has started
http:request: Application has received an incoming HTTP request.
We could easily implement this
App behavior in
var Understudy = require'understudy';var App = moduleexports =Understudycallthis;;//// Starts the application after running before and// after hooks.//thisperform'start' options//// Here, `options` may have been mutated from the execution// of the before hooks.// ...// Do some other async thing// ...// These arguments are passed to the final callback unless// short-circuited by an error here, or in an after hook.//nextnull options;callback;;reqtimes =start: processhrtime;thisperform'http:request' req resreqtimesmiddle = processhrtime;reqtimesbegin = processhrtimereqtimesstart;next;if err//// Do some error handling.//reqtimestotal = processhrtimereqtimesstart;reqtimesafter = processhrtimereqtimesmiddle;console.log'Total time: %s'' Before hooks: %s'' After hooks: %s'join'\n' formatreqtimestotal formatreqtimesbegin formatreqtimesafter;resend;;//// Now we consume a new app with hooks.//var http = require'http';var app = ;appbefore'start'var server = optionsserver = httpcreateServerapphandlereq res;;serverlistenoptionsport next;;appafter'start'console.log'App started on %s' optionsport;;appbefore'http:request'//// Do something asynchronous.//next;;appstart port: 8080console.log'ok';;//// Format process.hrtime()//return s0 * 1e3 + s1 / 1e6 / 1e3;
after hook can provide an optional error to short-circuit evaluation of the flow that would normally follow it. This error will be provided to your
callback, when supplied. In the event that you DO NOT provide a
callback and a
work function responds with an
Error IT WILL BE IGNORED AND FLOW WILL CONTINUE. e.g.
var Understudy = require'understudy';var actor = ;actorbefore'always'next'I always fail';;actorafter'always'console.log'I always get called. NO MATTER WHAT';console.log'BUT, only when no callback is supplied.';next'Another swallowed error';;actorperform'always'done'Errors are ignored here too.';;
In other words (as in the above example): if you do not supply a callback to your
understudy will consider all of your
work functions as "fire and forget".