nue — An async control-flow library
nue is an async control-flow library suited for node.js.
Installing
$ npm install nue
Example
var flow = flow;var as = as;var fs = ; var myFlow = { fs; fs; } { thisnextdata1 + data2; } { if thiserr throw thiserr; console; console; thisnext; }; ;
API
Top Level API
The nue
module provides following API.
flow([Function steps...]) -> Function
Return a function which represents the control-flow.
steps
: Optional. Optional functions to execute in series.
flow(String flowName) -> Function
Accept a flow name and return another flow
function.
flowName
: Required. A flow name to be used for debug.
parallel([Function steps...]) -> Function
Return a function which represents the parallel control-flow.
The parallel
must be nested inside a flow
or another parallel
.
steps
: Optional. Optional functions to execute in parallel.
parallel(String flowName) -> Function
Accept a flow name and return another parallel
function.
flowName
: Required. A flow name to be used for debug.
as(Number index) -> Object
index
: Required. An index to map an asynchronous callback argument to a next step argument. If the index is zero, an error handling is skipped.
Step Context API
flow
and parallel
API accept functions called step
s. Each step context object - it means a this
object in the step function - provides following API.
next([Object values...]) -> Void
A function to execute a next step immediately.
values
: Optional. Arguments for a next step.
async([Object mapping]) -> Function
A function to accept an argument mapping definition for a next step and return a callback.
async
can be called many times, but all calls are done in same tick.
And all callbacks async
returns must be called.
mapping
: Optional. An argument mapping definition.
To map single argument, call as
API and pass its result.
fs;
To map multiple arguments, pass an object.
child_process;
To map multiple arguments except first one, pass no object. You can get result arguments by index.
child_process;
asyncEach(Array array, Function callback(element, group, index, traversedArray)) -> Void
A function to execute a provided function once per array element asynchronously.
array
: Required. An array.callback
: Required. A function being executed once per array element. The context object in the callback is same with outer step context object.element
: Required. A current element.group
: Required. Provedesasync
function to accept an argument mapping definition and return a callback.index
: Required. An element index.traversedArray
: Required. An array object being traversed.
asyncEach(Number concurrency) -> Function
A function to accept a concurrency number and return another asyncEach
function which
executes a provided function once per array element asynchronously with the specified cuncurrency.
If you use another asyncEach
function directly, default concurrency 10 is used.
concurrency
: Required. the number of concurrency.
exec(Function function, [Object args...], Function callback(err, [values...])) -> Void
A function to execute a specified function
with args
asynchronously.
function
: Required. A function to be executed asynchronously.args
: Optional. Arguments for thefunction
.callback
: Required. A function to be executed when thefunction
is completed.err
: Required. An error in an async call.values
: Required. Results from thefunction
.
end([Object values...]) -> Void
A function to execute a last step immediately to end a control-flow.
values
: Optional. Arguments for a last step.
endWith(Error err) -> Void
A function to execute a last step immediately with an error to end a control-flow.
err
: Required. An error object. This object can be referred asthis.err
in a last step.
data : Object
A object to share arbitrary data between steps in a control-flow.
flowName : String
A flow name.
stepName : String
A step name.
err : Object
An error object, which is thrown with throw
, passed to this.endWith
or passed to an async callback as first argument.
This property is accessible in only last steps.
More Examples
Arguments Passing Between Functions
Arguments are passed with this.next
or this.async
.
Synchronously
var flow = flow; var myFlow = { var length = s1length + s2length thisnexts1 s2 length; } { if thiserr throw thiserr; console; // file1.length + file2.length -> 10 console; thisnext; }; ;
Asynchronously
To pass asynchronous call results to a next function, arguments mapping definition is necessary.
The function as
accepts an index to specify a callback argument and returns arguments mapping definition.
The function this.async
accepts the mapping definition and return a callback.
When all callbacks are completed, the next function is called with specific arguments.
var flow = flow;var as = as;var fs = ; var myFlow = { fs; fs; } { if thiserr throw thiserr; console; // FILE1FILE2 console; thisnext; }; ;
Arguments mapping definition can contain arbitrary values.
var flow = flow;var as = as;var fs = ; var myFlow = { fs; fs; } { if thiserr throw thiserr; console; // file1 and file2 have been read. console; // FILE1FILE2 console; thisnext; }; ;
Asynchronous Loop
this.asyncEach
executes a provided function once per array element asynchronously.
By default, the number of concurrency is 10.
var flow = flow;var as = as;var fs = ; var myFlow = { this; } { if thiserr throw thiserr; var names = files; var contents = files; console; // file1 and file2 have been read. console; // FILE1FILE2 thisnext; }; ;
To change the number of concurrency, specify the number as below.
{ thisfiles { ... }; }
Flow Nesting
A flow is composable. So it can be nested.
var flow = flow;var as = as;var fs = ; var subFlow = { fs; }; var mainFlow = { thisnext'file1'; } subFlow { if thiserr throw thiserr; console; console; thisnext; }; ;
Asynchronous Flow Execution
A flow can be executed asynchronously.
var flow = flow;var as = as;var fs = ; var subFlow = { fs; }; var mainFlow = { this; this; } { if thiserr throw thiserr; console; console; thisnext; }; ;
Parallel Flow
In following example, the flow par1-1
and par1-2
are executed in parallel.
var flow = flow;var parallel = parallel; var myFlow = { console; thisnext; } { console; thisnext; } { console; thisnext; } { console; thisnext; } { console; thisnext; } { console; thisnext; } { console; thisnext; } { console; thisnext; } { if thiserr throw thiserr; console; thisnext; }; ;
Arguments to a parallel flow are passed to every forked functions. Parallel flow results are passed to a next funtion as an array. The array contains the results of forked functions.
var flow = flow;var parallel = parallel; var myFlow = { thisnext10 20; } { thisnextx + y; } { thisnextx - y; } { if thiserr throw thiserr; console; // add result: 30 console; // sub result: -10 thisnext; }; ;
Data Sharing Between Functions
Each step in a flow can share data through this.data
.
this.data
is shared in a same flow.
A nesting flow and any nested flows can't share this.data
.
var flow = flow;var as = as;var fs = ; var myFlow = { thisdatafile1 = file1; thisdatafile2 = file2; fs; fs; } { thisnextdata1 + data2; } { if thiserr throw thiserr; console; console; thisnext; }; ;
Error Handling
In a last step in a flow, this.err
represents an error which is thrown with throw
, passed to this.endWith
or passed to an async callback as first argument.
To indicate error handling is completed, you must assign null
to this.err
.
var flow = flow;var as = as;var fs = ; var myFlow = { fs; fs; } { thisnextdata1 + data2; } { if thiserr // handle error console; // indicate error handling completion thiserr = null; else console; console; thisnext; }; ;
Unit Test with Mocha
Following example shows how to test a flow and a function with Mocha.
var flow = flow;var as = as;var fs = ; var concatFiles = ; { fs;} var assert = ; ; ;
Debugging
Use NODE_DEBUG=nue
.
Example
hoge.js
var flow = flow; { thisnextx + y; } { if thiserr throw thiserr; console; }10 20;
Run and Output
$ NODE_DEBUG=nue node hoge.jsNUE: begin TOP_LEVEL_FLOW. flow: hoge, calledAt: /private/tmp/hoge.js:11:1, args: [ 10, 20 ]NUE: begin STEP. flow: hoge, step: add, args: [ 10, 20 ]NUE: begin STEP. flow: hoge, step: done, args: [ 30 ]30