Package Exports
- scope-chain
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (scope-chain) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
scope-chain
Another asynchronous function chain wrapper offering both middleware and non-middleware uses
var app = require('express')();
var chain = require('scope-chain');
var fs = require('fs');
app.get('/', function (req, res, next) {
    var scopeVariable = Date() + '\n';
    chain(next, function step() {
        res.write(scopeVariable, this);
        
    }, function step() {
        setTimeout(this, 2000);
        
    }, function step() {
        res.write(Date() + '\n', this);
        
    });
}, function (req, res, next) {
    res.end();
}).listen(3000);Installation
$ npm install scope-chainFeatures
- Encourages middleware code-layouts presenting a clear birds-eye view
- Flexible next-step callback functionality
- Uncluttered usage lets the code tell it's own story
- Invisible testing of errarguments
- invisible try/catch wrapping of each step function
- string final arg used as informative error prefix
- optional additional thisattributes as 1st arg
Philosophy
Programming a multi-step activity in the asynchronous NodeJS architecture can lead to a staircase of in-line functions as in this contrived example.
function middleware(req, res, next) {
    res.setHeader('Content-Type', 'text/plain');
    fs.readdir('/var/run', function (err, files) {
        for (var file in files) {
            fs.readyFile('/var/run/' + file, function (err, data) {
            });
            ... etc - async gets messy here
        }
    });
}This scope-chain is for those who prefer to see code as a more linear progression.
function middleware(req, res, next) {
    res.setHeader('Content-Type', 'text/plain');
    chain({ path: '/var/run' }, next, function () {
        fs.readdir(this.path, this); // cb(err1, files)
    }, function callee(files, file,  err2, data) {
        if (file && !err2) // 
            res.write(file + ': ' + data.trim() + '\n');
        if (!files.length)
            return this();
        var file = files.shift();
        fs.readFile(this.path + '/' + file, callee.bind(this, files, file)); // cb(err2, data)
        
   });
}Detail
The chain function has the signature chain(?ctx, final, step, step, step, ...)
The optional ctx argument chain is described after final is described.
The 1st mandatory argument to chain is the final, and is either a string or
a function having a signature of function (err, args, ...). As a convenience,
when final is provided as a string, it is transformed into a function as
shown below. This convenience is intended to help identify the code location
where an Error originates.
function (err) {
    err && console.error(this + (err.stack || err));
}.bind(final + ' ')The provided OR auto-generated final is is always called last. In a middleware usage, the middleware-next argument is usually supplied here.
The optional ctx argument provides a means to add attrubutes to the this
function-object passed to each step. Any enumerable attributes of the
optional ctx argument are added to the final function, which are then
subject to Attribute Copying as described below.
The remaining step arguments are the sequential activities of the chain.
When called, each function is wrapped in its own try/catch where all exceptions
are caught and sent to the final function, aborting all subsequent steps. For
each of the step functions this is a function-object to be used as the
inner-next for each step function.
The this object provides four variants of the inner-next functionality having 
the following signatures:
this(err, arg1, arg2, arg3, ...)
When this() is called with a falsy err value, the subsequent step function 
is called with just the available argN arguments.
When called with a truthy err value, the final function is called with that truthy err value, thereby aborting the step sequence.
this.silent(err, arg1, arg2, arg3, ...)
When this.silent() is called with a falsy err value, the subsequent step 
function is called with just the available argN arguments.
When called with a truthy err value, the final function is called with no arguments so discarding the error but also aborting the step sequence.
this.ignore(err, arg1, arg2, arg3, ...)
When this.ignore() is called with a falsy err value, the subsequent step 
function is called with just the available argN arguments.
When called with a truthy err value, the error is discarded and the subsequent 
step function is called with just the available argN arguments.
this.noerror(arg1, arg2, arg3, ...)
As this.noerror() has no concept of an err argument, the subsequent step 
function is called with all the available argN arguments. Arg1 may be an err
value, but the chain functionality is ignorant of this.
Attribute Copying
Any attributes found on the provided OR autogenerated final function are copied to each of the four variant inner-next functions, but NOT copied back on completion. This can useful is passing context to private implementations of asynchronous logic.
The author used attribute-copying in an ExpressJS application to attach a 
unique index to each req object so that log-lines from interleaved requests 
could be attributed to the original ExpressJS request.
app.use(function (req, res, next) {
    req.index = ++global.index; next();
});
app.get('/', function (req, res, next) {
    chain({ index: req.index }, next, function () {
        mysql(sql, this);
        
    }, function (rows, meta) {
        ...
        
    });
});
function mysql(sql, callback) { // cb(err, rows, cols)
    debug(callback.index, sql, ...);
    ...
}On occassions, a step yields an array result where a subsequent step needs to be invoked for each array member. For example, a first step may use a sql select to return an array or rows, and the next step must update each row individually.
app.get('/', function (req, res, next) {
    chain(next, function step() { // chain-A
        mysql('select * from database.table where uuid is null', this);
        
    }, function callee(rows, cols) {
        if (!rows.length) // all-done
            return this();
            
        var row = rows.shift();
        chain(this, function () { // chain-B
            mysql('update database.table set uuid=? where id=?', [uuid(), row.id], this);
            
        }, function inner() {
            callee.call(this.this, rows, cols); // repeat for next row
            // `this` is the this-object of `inner`, the step of chain-B
            // `this.this` is the this-object of `callee`, the step of chain-A
            
        });
        
    }, function alldone() {
        this();
    });
});