Plato on Github
Report Home
node_modules/when/node.js
Maintainability
78.40
Lines of code
277
Difficulty
41.83
Estimated Errors
0.87
Function weight
By Complexity
By SLOC
/** @license MIT License (c) copyright 2013 original author or authors */ /** * Collection of helpers for interfacing with node-style asynchronous functions * using promises. * * @author Brian Cavalier * @contributor Renato Zannon */ (function(define) { define(function(require) { var when = require('./when'); var Promise = when.Promise; var _liftAll = require('./lib/liftAll'); var setTimer = require('./lib/timer').set; var slice = Array.prototype.slice; return { lift: lift, liftAll: liftAll, apply: apply, call: call, createCallback: createCallback, bindCallback: bindCallback, liftCallback: liftCallback }; /** * Takes a node-style async function and calls it immediately (with an optional * array of arguments or promises for arguments). It returns a promise whose * resolution depends on whether the async functions calls its callback with the * conventional error argument or not. * * With this it becomes possible to leverage existing APIs while still reaping * the benefits of promises. * * @example * function onlySmallNumbers(n, callback) { * if(n < 10) { * callback(null, n + 10); * } else { * callback(new Error("Calculation failed")); * } * } * * var nodefn = require("when/node/function"); * * // Logs '15' * nodefn.apply(onlySmallNumbers, [5]).then(console.log, console.error); * * // Logs 'Calculation failed' * nodefn.apply(onlySmallNumbers, [15]).then(console.log, console.error); * * @param {function} f node-style function that will be called * @param {Array} [args] array of arguments to func * @returns {Promise} promise for the value func passes to its callback */ function apply(f, args) { return run(f, this, args || []); } /** * Apply helper that allows specifying thisArg * @private */ function run(f, thisArg, args) { var p = Promise._defer(); switch(args.length) { case 2: apply2(p, f, thisArg, args); break; case 1: apply1(p, f, thisArg, args); break; default: applyN(p, f, thisArg, args); } return p; } function applyN(p, f, thisArg, args) { Promise.all(args)._handler.chain(thisArg, function(args) { args.push(createCallback(p._handler)); f.apply(this, args); }); } function apply2(p, f, thisArg, args) { Promise.resolve(args[0]).then(function(x) { Promise.resolve(args[1])._handler.chain(thisArg, function(y) { f.call(this, x, y, createCallback(p._handler)); }); }); } function apply1(p, f, thisArg, args) { Promise.resolve(args[0])._handler.chain(thisArg, function(x) { f.call(this, x, createCallback(p._handler)); }); } /** * Has the same behavior that {@link apply} has, with the difference that the * arguments to the function are provided individually, while {@link apply} accepts * a single array. * * @example * function sumSmallNumbers(x, y, callback) { * var result = x + y; * if(result < 10) { * callback(null, result); * } else { * callback(new Error("Calculation failed")); * } * } * * // Logs '5' * nodefn.call(sumSmallNumbers, 2, 3).then(console.log, console.error); * * // Logs 'Calculation failed' * nodefn.call(sumSmallNumbers, 5, 10).then(console.log, console.error); * * @param {function} f node-style function that will be called * @param {...*} [args] arguments that will be forwarded to the function * @returns {Promise} promise for the value func passes to its callback */ function call(f /*, args... */) { return run(f, this, slice.call(arguments, 1)); } /** * Takes a node-style function and returns new function that wraps the * original and, instead of taking a callback, returns a promise. Also, it * knows how to handle promises given as arguments, waiting for their * resolution before executing. * * Upon execution, the orginal function is executed as well. If it passes * a truthy value as the first argument to the callback, it will be * interpreted as an error condition, and the promise will be rejected * with it. Otherwise, the call is considered a resolution, and the promise * is resolved with the callback's second argument. * * @example * var fs = require("fs"), nodefn = require("when/node/function"); * * var promiseRead = nodefn.lift(fs.readFile); * * // The promise is resolved with the contents of the file if everything * // goes ok * promiseRead('exists.txt').then(console.log, console.error); * * // And will be rejected if something doesn't work out * // (e.g. the files does not exist) * promiseRead('doesnt_exist.txt').then(console.log, console.error); * * * @param {Function} f node-style function to be lifted * @param {...*} [args] arguments to be prepended for the new function @deprecated * @returns {Function} a promise-returning function */ function lift(f /*, args... */) { var args = arguments.length > 1 ? slice.call(arguments, 1) : []; return function() { return run(f, this, args.concat(slice.call(arguments))); }; } /** * Lift all the functions/methods on src * @param {object|function} src source whose functions will be lifted * @param {function?} combine optional function for customizing the lifting * process. It is passed dst, the lifted function, and the property name of * the original function on src. * @param {(object|function)?} dst option destination host onto which to place lifted * functions. If not provided, liftAll returns a new object. * @returns {*} If dst is provided, returns dst with lifted functions as * properties. If dst not provided, returns a new object with lifted functions. */ function liftAll(src, combine, dst) { return _liftAll(lift, combine, dst, src); } /** * Takes an object that responds to the resolver interface, and returns * a function that will resolve or reject it depending on how it is called. * * @example * function callbackTakingFunction(callback) { * if(somethingWrongHappened) { * callback(error); * } else { * callback(null, interestingValue); * } * } * * var when = require('when'), nodefn = require('when/node/function'); * * var deferred = when.defer(); * callbackTakingFunction(nodefn.createCallback(deferred.resolver)); * * deferred.promise.then(function(interestingValue) { * // Use interestingValue * }); * * @param {Resolver} resolver that will be 'attached' to the callback * @returns {Function} a node-style callback function */ function createCallback(resolver) { return function(err, value) { if(err) { resolver.reject(err); } else if(arguments.length > 2) { resolver.resolve(slice.call(arguments, 1)); } else { resolver.resolve(value); } }; } /** * Attaches a node-style callback to a promise, ensuring the callback is * called for either fulfillment or rejection. Returns a promise with the same * state as the passed-in promise. * * @example * var deferred = when.defer(); * * function callback(err, value) { * // Handle err or use value * } * * bindCallback(deferred.promise, callback); * * deferred.resolve('interesting value'); * * @param {Promise} promise The promise to be attached to. * @param {Function} callback The node-style callback to attach. * @returns {Promise} A promise with the same state as the passed-in promise. */ function bindCallback(promise, callback) { promise = when(promise); if (callback) { promise.then(success, wrapped); } return promise; function success(value) { wrapped(null, value); } function wrapped(err, value) { setTimer(function () { callback(err, value); }, 0); } } /** * Takes a node-style callback and returns new function that accepts a * promise, calling the original callback when the promise is either * fulfilled or rejected with the appropriate arguments. * * @example * var deferred = when.defer(); * * function callback(err, value) { * // Handle err or use value * } * * var wrapped = liftCallback(callback); * * // `wrapped` can now be passed around at will * wrapped(deferred.promise); * * deferred.resolve('interesting value'); * * @param {Function} callback The node-style callback to wrap. * @returns {Function} The lifted, promise-accepting function. */ function liftCallback(callback) { return function(promise) { return bindCallback(promise, callback); }; } }); })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); });