aboutsummaryrefslogtreecommitdiff
path: root/node_modules/when/callbacks.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/when/callbacks.js')
-rw-r--r--node_modules/when/callbacks.js262
1 files changed, 262 insertions, 0 deletions
diff --git a/node_modules/when/callbacks.js b/node_modules/when/callbacks.js
new file mode 100644
index 000000000..00e414b51
--- /dev/null
+++ b/node_modules/when/callbacks.js
@@ -0,0 +1,262 @@
+/** @license MIT License (c) copyright 2013-2014 original author or authors */
+
+/**
+ * Collection of helper functions for interacting with 'traditional',
+ * callback-taking functions using a promise interface.
+ *
+ * @author Renato Zannon
+ * @contributor Brian Cavalier
+ */
+
+(function(define) {
+define(function(require) {
+
+ var when = require('./when');
+ var Promise = when.Promise;
+ var _liftAll = require('./lib/liftAll');
+ var slice = Array.prototype.slice;
+
+ var makeApply = require('./lib/apply');
+ var _apply = makeApply(Promise, dispatch);
+
+ return {
+ lift: lift,
+ liftAll: liftAll,
+ apply: apply,
+ call: call,
+ promisify: promisify
+ };
+
+ /**
+ * Takes a `traditional` callback-taking function and returns a promise for its
+ * result, accepting an optional array of arguments (that might be values or
+ * promises). It assumes that the function takes its callback and errback as
+ * the last two arguments. The resolution of the promise depends on whether the
+ * function will call its callback or its errback.
+ *
+ * @example
+ * var domIsLoaded = callbacks.apply($);
+ * domIsLoaded.then(function() {
+ * doMyDomStuff();
+ * });
+ *
+ * @example
+ * function existingAjaxyFunction(url, callback, errback) {
+ * // Complex logic you'd rather not change
+ * }
+ *
+ * var promise = callbacks.apply(existingAjaxyFunction, ["/movies.json"]);
+ *
+ * promise.then(function(movies) {
+ * // Work with movies
+ * }, function(reason) {
+ * // Handle error
+ * });
+ *
+ * @param {function} asyncFunction function to be called
+ * @param {Array} [extraAsyncArgs] array of arguments to asyncFunction
+ * @returns {Promise} promise for the callback value of asyncFunction
+ */
+ function apply(asyncFunction, extraAsyncArgs) {
+ return _apply(asyncFunction, this, extraAsyncArgs || []);
+ }
+
+ /**
+ * Apply helper that allows specifying thisArg
+ * @private
+ */
+ function dispatch(f, thisArg, args, h) {
+ args.push(alwaysUnary(h.resolve, h), alwaysUnary(h.reject, h));
+ tryCatchResolve(f, thisArg, args, h);
+ }
+
+ function tryCatchResolve(f, thisArg, args, resolver) {
+ try {
+ f.apply(thisArg, args);
+ } catch(e) {
+ resolver.reject(e);
+ }
+ }
+
+ /**
+ * Works as `callbacks.apply` does, with the difference that the arguments to
+ * the function are passed individually, instead of as an array.
+ *
+ * @example
+ * function sumInFiveSeconds(a, b, callback) {
+ * setTimeout(function() {
+ * callback(a + b);
+ * }, 5000);
+ * }
+ *
+ * var sumPromise = callbacks.call(sumInFiveSeconds, 5, 10);
+ *
+ * // Logs '15' 5 seconds later
+ * sumPromise.then(console.log);
+ *
+ * @param {function} asyncFunction function to be called
+ * @param {...*} args arguments that will be forwarded to the function
+ * @returns {Promise} promise for the callback value of asyncFunction
+ */
+ function call(asyncFunction/*, arg1, arg2...*/) {
+ return _apply(asyncFunction, this, slice.call(arguments, 1));
+ }
+
+ /**
+ * Takes a 'traditional' callback/errback-taking function and returns a function
+ * that returns a promise instead. The resolution/rejection of the promise
+ * depends on whether the original function will call its callback or its
+ * errback.
+ *
+ * If additional arguments are passed to the `lift` call, they will be prepended
+ * on the calls to the original function, much like `Function.prototype.bind`.
+ *
+ * The resulting function is also "promise-aware", in the sense that, if given
+ * promises as arguments, it will wait for their resolution before executing.
+ *
+ * @example
+ * function traditionalAjax(method, url, callback, errback) {
+ * var xhr = new XMLHttpRequest();
+ * xhr.open(method, url);
+ *
+ * xhr.onload = callback;
+ * xhr.onerror = errback;
+ *
+ * xhr.send();
+ * }
+ *
+ * var promiseAjax = callbacks.lift(traditionalAjax);
+ * promiseAjax("GET", "/movies.json").then(console.log, console.error);
+ *
+ * var promiseAjaxGet = callbacks.lift(traditionalAjax, "GET");
+ * promiseAjaxGet("/movies.json").then(console.log, console.error);
+ *
+ * @param {Function} f traditional async function to be decorated
+ * @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 _apply(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);
+ }
+
+ /**
+ * `promisify` is a version of `lift` that allows fine-grained control over the
+ * arguments that passed to the underlying function. It is intended to handle
+ * functions that don't follow the common callback and errback positions.
+ *
+ * The control is done by passing an object whose 'callback' and/or 'errback'
+ * keys, whose values are the corresponding 0-based indexes of the arguments on
+ * the function. Negative values are interpreted as being relative to the end
+ * of the arguments array.
+ *
+ * If arguments are given on the call to the 'promisified' function, they are
+ * intermingled with the callback and errback. If a promise is given among them,
+ * the execution of the function will only occur after its resolution.
+ *
+ * @example
+ * var delay = callbacks.promisify(setTimeout, {
+ * callback: 0
+ * });
+ *
+ * delay(100).then(function() {
+ * console.log("This happens 100ms afterwards");
+ * });
+ *
+ * @example
+ * function callbackAsLast(errback, followsStandards, callback) {
+ * if(followsStandards) {
+ * callback("well done!");
+ * } else {
+ * errback("some programmers just want to watch the world burn");
+ * }
+ * }
+ *
+ * var promisified = callbacks.promisify(callbackAsLast, {
+ * callback: -1,
+ * errback: 0,
+ * });
+ *
+ * promisified(true).then(console.log, console.error);
+ * promisified(false).then(console.log, console.error);
+ *
+ * @param {Function} asyncFunction traditional function to be decorated
+ * @param {object} positions
+ * @param {number} [positions.callback] index at which asyncFunction expects to
+ * receive a success callback
+ * @param {number} [positions.errback] index at which asyncFunction expects to
+ * receive an error callback
+ * @returns {function} promisified function that accepts
+ *
+ * @deprecated
+ */
+ function promisify(asyncFunction, positions) {
+
+ return function() {
+ var thisArg = this;
+ return Promise.all(arguments).then(function(args) {
+ var p = Promise._defer();
+
+ var callbackPos, errbackPos;
+
+ if(typeof positions.callback === 'number') {
+ callbackPos = normalizePosition(args, positions.callback);
+ }
+
+ if(typeof positions.errback === 'number') {
+ errbackPos = normalizePosition(args, positions.errback);
+ }
+
+ if(errbackPos < callbackPos) {
+ insertCallback(args, errbackPos, p._handler.reject, p._handler);
+ insertCallback(args, callbackPos, p._handler.resolve, p._handler);
+ } else {
+ insertCallback(args, callbackPos, p._handler.resolve, p._handler);
+ insertCallback(args, errbackPos, p._handler.reject, p._handler);
+ }
+
+ asyncFunction.apply(thisArg, args);
+
+ return p;
+ });
+ };
+ }
+
+ function normalizePosition(args, pos) {
+ return pos < 0 ? (args.length + pos + 2) : pos;
+ }
+
+ function insertCallback(args, pos, callback, thisArg) {
+ if(typeof pos === 'number') {
+ args.splice(pos, 0, alwaysUnary(callback, thisArg));
+ }
+ }
+
+ function alwaysUnary(fn, thisArg) {
+ return function() {
+ if (arguments.length > 1) {
+ fn.call(thisArg, slice.call(arguments));
+ } else {
+ fn.apply(thisArg, arguments);
+ }
+ };
+ }
+});
+})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); });