| /** @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 _liftAll = require('./lib/liftAll'); |
| var setTimer = require('./lib/env').setTimer; |
| var slice = Array.prototype.slice; |
| |
| var _apply = require('./lib/apply')(when.Promise, dispatch); |
| |
| 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 _apply(f, this, args || []); |
| } |
| |
| function dispatch(f, thisArg, args, h) { |
| var cb = createCallback(h); |
| try { |
| switch(args.length) { |
| case 2: f.call(thisArg, args[0], args[1], cb); break; |
| case 1: f.call(thisArg, args[0], cb); break; |
| case 0: f.call(thisArg, cb); break; |
| default: |
| args.push(cb); |
| f.apply(thisArg, args); |
| } |
| } catch(e) { |
| h.reject(e); |
| } |
| } |
| |
| /** |
| * 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 _apply(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 args1 = arguments.length > 1 ? slice.call(arguments, 1) : []; |
| return function() { |
| // TODO: Simplify once partialing has been removed |
| var l = args1.length; |
| var al = arguments.length; |
| var args = new Array(al + l); |
| var i; |
| for(i=0; i<l; ++i) { |
| args[i] = args1[i]; |
| } |
| for(i=0; i<al; ++i) { |
| args[i+l] = arguments[i]; |
| } |
| return _apply(f, this, args); |
| }; |
| } |
| |
| /** |
| * 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); }); |
| |
| |
| |