node_modules/when/node.js

Maintainability

78.40

Lines of code

277

Created with Raphaël 2.1.002550751002015-1-52014-12-42014-12-3

2015-5-18
Maintainability: 78.4

Created with Raphaël 2.1.00751502253002015-1-52014-12-42014-12-3

2015-5-18
Lines of Code: 277

Difficulty

41.83

Estimated Errors

0.87

Function weight

By Complexity

Created with Raphaël 2.1.0run3

By SLOC

Created with Raphaël 2.1.0<anonymous>277
1
/** @license MIT License (c) copyright 2013 original author or authors */
2
 
3
/**
4
 * Collection of helpers for interfacing with node-style asynchronous functions
5
 * using promises.
6
 *
7
 * @author Brian Cavalier
8
 * @contributor Renato Zannon
9
 */
10
 
11
(function(define) {
12
define(function(require) {
13
 
14
    var when = require('./when');
15
    var Promise = when.Promise;
16
    var _liftAll = require('./lib/liftAll');
17
    var setTimer = require('./lib/timer').set;
18
    var slice = Array.prototype.slice;
19
 
20
    return {
21
        lift: lift,
22
        liftAll: liftAll,
23
        apply: apply,
24
        call: call,
25
        createCallback: createCallback,
26
        bindCallback: bindCallback,
27
        liftCallback: liftCallback
28
    };
29
 
30
    /**
31
     * Takes a node-style async function and calls it immediately (with an optional
32
     * array of arguments or promises for arguments). It returns a promise whose
33
     * resolution depends on whether the async functions calls its callback with the
34
     * conventional error argument or not.
35
     *
36
     * With this it becomes possible to leverage existing APIs while still reaping
37
     * the benefits of promises.
38
     *
39
     * @example
40
     *    function onlySmallNumbers(n, callback) {
41
     *      if(n < 10) {
42
     *          callback(null, n + 10);
43
     *      } else {
44
     *          callback(new Error("Calculation failed"));
45
     *      }
46
     *  }
47
     *
48
     *    var nodefn = require("when/node/function");
49
     *
50
     *    // Logs '15'
51
     *    nodefn.apply(onlySmallNumbers, [5]).then(console.log, console.error);
52
     *
53
     *    // Logs 'Calculation failed'
54
     *    nodefn.apply(onlySmallNumbers, [15]).then(console.log, console.error);
55
     *
56
     * @param {function} f node-style function that will be called
57
     * @param {Array} [args] array of arguments to func
58
     * @returns {Promise} promise for the value func passes to its callback
59
     */
60
    function apply(f, args) {
61
        return run(f, this, args || []);
62
    }
63
 
64
    /**
65
     * Apply helper that allows specifying thisArg
66
     * @private
67
     */
68
    function run(f, thisArg, args) {
69
        var p = Promise._defer();
70
        switch(args.length) {
71
            case 2:  apply2(p, f, thisArg, args); break;
72
            case 1:  apply1(p, f, thisArg, args); break;
73
            default: applyN(p, f, thisArg, args);
74
        }
75
 
76
        return p;
77
    }
78
 
79
    function applyN(p, f, thisArg, args) {
80
        Promise.all(args)._handler.chain(thisArg, function(args) {
81
            args.push(createCallback(p._handler));
82
            f.apply(this, args);
83
        });
84
    }
85
 
86
    function apply2(p, f, thisArg, args) {
87
        Promise.resolve(args[0]).then(function(x) {
88
            Promise.resolve(args[1])._handler.chain(thisArg, function(y) {
89
                f.call(this, x, y, createCallback(p._handler));
90
            });
91
        });
92
    }
93
 
94
    function apply1(p, f, thisArg, args) {
95
        Promise.resolve(args[0])._handler.chain(thisArg, function(x) {
96
            f.call(this, x, createCallback(p._handler));
97
        });
98
    }
99
 
100
    /**
101
     * Has the same behavior that {@link apply} has, with the difference that the
102
     * arguments to the function are provided individually, while {@link apply} accepts
103
     * a single array.
104
     *
105
     * @example
106
     *    function sumSmallNumbers(x, y, callback) {
107
     *      var result = x + y;
108
     *      if(result < 10) {
109
     *          callback(null, result);
110
     *      } else {
111
     *          callback(new Error("Calculation failed"));
112
     *      }
113
     *  }
114
     *
115
     *    // Logs '5'
116
     *    nodefn.call(sumSmallNumbers, 2, 3).then(console.log, console.error);
117
     *
118
     *    // Logs 'Calculation failed'
119
     *    nodefn.call(sumSmallNumbers, 5, 10).then(console.log, console.error);
120
     *
121
     * @param {function} f node-style function that will be called
122
     * @param {...*} [args] arguments that will be forwarded to the function
123
     * @returns {Promise} promise for the value func passes to its callback
124
     */
125
    function call(f /*, args... */) {
126
        return run(f, this, slice.call(arguments, 1));
127
    }
128
 
129
    /**
130
     * Takes a node-style function and returns new function that wraps the
131
     * original and, instead of taking a callback, returns a promise. Also, it
132
     * knows how to handle promises given as arguments, waiting for their
133
     * resolution before executing.
134
     *
135
     * Upon execution, the orginal function is executed as well. If it passes
136
     * a truthy value as the first argument to the callback, it will be
137
     * interpreted as an error condition, and the promise will be rejected
138
     * with it. Otherwise, the call is considered a resolution, and the promise
139
     * is resolved with the callback's second argument.
140
     *
141
     * @example
142
     *    var fs = require("fs"), nodefn = require("when/node/function");
143
     *
144
     *    var promiseRead = nodefn.lift(fs.readFile);
145
     *
146
     *    // The promise is resolved with the contents of the file if everything
147
     *    // goes ok
148
     *    promiseRead('exists.txt').then(console.log, console.error);
149
     *
150
     *    // And will be rejected if something doesn't work out
151
     *    // (e.g. the files does not exist)
152
     *    promiseRead('doesnt_exist.txt').then(console.log, console.error);
153
     *
154
     *
155
     * @param {Function} f node-style function to be lifted
156
     * @param {...*} [args] arguments to be prepended for the new function @deprecated
157
     * @returns {Function} a promise-returning function
158
     */
159
    function lift(f /*, args... */) {
160
        var args = arguments.length > 1 ? slice.call(arguments, 1) : [];
161
        return function() {
162
            return run(f, this, args.concat(slice.call(arguments)));
163
        };
164
    }
165
 
166
    /**
167
     * Lift all the functions/methods on src
168
     * @param {object|function} src source whose functions will be lifted
169
     * @param {function?} combine optional function for customizing the lifting
170
     *  process. It is passed dst, the lifted function, and the property name of
171
     *  the original function on src.
172
     * @param {(object|function)?} dst option destination host onto which to place lifted
173
     *  functions. If not provided, liftAll returns a new object.
174
     * @returns {*} If dst is provided, returns dst with lifted functions as
175
     *  properties.  If dst not provided, returns a new object with lifted functions.
176
     */
177
    function liftAll(src, combine, dst) {
178
        return _liftAll(lift, combine, dst, src);
179
    }
180
 
181
    /**
182
     * Takes an object that responds to the resolver interface, and returns
183
     * a function that will resolve or reject it depending on how it is called.
184
     *
185
     * @example
186
     *  function callbackTakingFunction(callback) {
187
     *      if(somethingWrongHappened) {
188
     *          callback(error);
189
     *      } else {
190
     *          callback(null, interestingValue);
191
     *      }
192
     *  }
193
     *
194
     *  var when = require('when'), nodefn = require('when/node/function');
195
     *
196
     *  var deferred = when.defer();
197
     *  callbackTakingFunction(nodefn.createCallback(deferred.resolver));
198
     *
199
     *  deferred.promise.then(function(interestingValue) {
200
     *      // Use interestingValue
201
     *  });
202
     *
203
     * @param {Resolver} resolver that will be 'attached' to the callback
204
     * @returns {Function} a node-style callback function
205
     */
206
    function createCallback(resolver) {
207
        return function(err, value) {
208
            if(err) {
209
                resolver.reject(err);
210
            } else if(arguments.length > 2) {
211
                resolver.resolve(slice.call(arguments, 1));
212
            } else {
213
                resolver.resolve(value);
214
            }
215
        };
216
    }
217
 
218
    /**
219
     * Attaches a node-style callback to a promise, ensuring the callback is
220
     * called for either fulfillment or rejection. Returns a promise with the same
221
     * state as the passed-in promise.
222
     *
223
     * @example
224
     *  var deferred = when.defer();
225
     *
226
     *  function callback(err, value) {
227
     *      // Handle err or use value
228
     *  }
229
     *
230
     *  bindCallback(deferred.promise, callback);
231
     *
232
     *  deferred.resolve('interesting value');
233
     *
234
     * @param {Promise} promise The promise to be attached to.
235
     * @param {Function} callback The node-style callback to attach.
236
     * @returns {Promise} A promise with the same state as the passed-in promise.
237
     */
238
    function bindCallback(promise, callback) {
239
        promise = when(promise);
240
 
241
        if (callback) {
242
            promise.then(success, wrapped);
243
        }
244
 
245
        return promise;
246
 
247
        function success(value) {
248
            wrapped(null, value);
249
        }
250
 
251
        function wrapped(err, value) {
252
            setTimer(function () {
253
                callback(err, value);
254
            }, 0);
255
        }
256
    }
257
 
258
    /**
259
     * Takes a node-style callback and returns new function that accepts a
260
     * promise, calling the original callback when the promise is either
261
     * fulfilled or rejected with the appropriate arguments.
262
     *
263
     * @example
264
     *  var deferred = when.defer();
265
     *
266
     *  function callback(err, value) {
267
     *      // Handle err or use value
268
     *  }
269
     *
270
     *  var wrapped = liftCallback(callback);
271
     *
272
     *  // `wrapped` can now be passed around at will
273
     *  wrapped(deferred.promise);
274
     *
275
     *  deferred.resolve('interesting value');
276
     *
277
     * @param {Function} callback The node-style callback to wrap.
278
     * @returns {Function} The lifted, promise-accepting function.
279
     */
280
    function liftCallback(callback) {
281
        return function(promise) {
282
            return bindCallback(promise, callback);
283
        };
284
    }
285
});
286
 
287
})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); });