wrapped
Version:
consistent interface for
113 lines (92 loc) • 1.89 kB
JavaScript
/**
* Module Dependencies
*/
var sliced = require('sliced');
var noop = function(){};
var co = require('co');
/**
* Export `wrapped`
*/
module.exports = wrapped;
/**
* Wrap a function to support
* sync, async, and gen functions.
*
* @param {Function} fn
* @return {Function}
* @api public
*/
function wrapped(fn) {
function wrap() {
var args = sliced(arguments);
var last = args[args.length - 1];
var ctx = this;
// done
var done = typeof last == 'function' ? args.pop() : noop;
// nothing
if (!fn) {
return done.apply(ctx, [null].concat(args));
}
// generator
if (generator(fn)) {
return co(fn).apply(ctx, args.concat(done));
}
// async
if (fn.length > args.length) {
// NOTE: this only handles uncaught synchronous errors
try {
return fn.apply(ctx, args.concat(done));
} catch (e) {
return done(e);
}
}
// sync
return sync(fn, done).apply(ctx, args);
}
return wrap;
}
/**
* Wrap a synchronous function execution.
*
* @param {Function} fn
* @param {Function} done
* @return {Function}
* @api private
*/
function sync(fn, done) {
return function () {
var ret;
try {
ret = fn.apply(this, arguments);
} catch (err) {
return done(err);
}
if (promise(ret)) {
ret.then(function (value) { done(null, value); }, done);
} else {
ret instanceof Error ? done(ret) : done(null, ret);
}
}
}
/**
* Is `value` a generator?
*
* @param {Mixed} value
* @return {Boolean}
* @api private
*/
function generator(value) {
return value
&& value.constructor
&& 'GeneratorFunction' == value.constructor.name;
}
/**
* Is `value` a promise?
*
* @param {Mixed} value
* @return {Boolean}
* @api private
*/
function promise(value) {
return value && 'function' == typeof value.then;
}