forked from cujojs/when
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfunction.js
212 lines (197 loc) · 6.16 KB
/
function.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/** @license MIT License (c) copyright 2013 original author or authors */
/**
* function.js
*
* Collection of helper functions for wrapping and executing 'traditional'
* synchronous functions in a promise interface.
*
* @author [email protected]
* @contributor [email protected]
*/
(function(define) {
define(function(require) {
var when, slice;
when = require('./when');
slice = [].slice;
return {
apply: apply,
call: call,
lift: lift,
bind: lift, // DEPRECATED alias for lift
compose: compose
};
/**
* Takes a function and an optional array of arguments (that might be promises),
* and calls the function. The return value is a promise whose resolution
* depends on the value returned by the function.
*
* @example
* function onlySmallNumbers(n) {
* if(n < 10) {
* return n + 10;
* } else {
* throw new Error("Calculation failed");
* }
* }
*
* // Logs '15'
* func.apply(onlySmallNumbers, [5]).then(console.log, console.error);
*
* // Logs 'Calculation failed'
* func.apply(onlySmallNumbers, [15]).then(console.log, console.error);
*
* @param {function} func function to be called
* @param {Array} [args] array of arguments to func
* @returns {Promise} promise for the return value of func
*/
function apply(func, promisedArgs) {
return _apply(func, this, promisedArgs);
}
/**
* Apply helper that allows specifying thisArg
* @private
*/
function _apply(func, thisArg, promisedArgs) {
return when.all(promisedArgs || [], function(args) {
return func.apply(thisArg, args);
});
}
/**
* 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) {
* var result = x + y;
* if(result < 10) {
* return result;
* } else {
* throw new Error("Calculation failed");
* }
* }
*
* // Logs '5'
* func.apply(sumSmallNumbers, 2, 3).then(console.log, console.error);
*
* // Logs 'Calculation failed'
* func.apply(sumSmallNumbers, 5, 10).then(console.log, console.error);
*
* @param {function} func function to be called
* @param {...*} [args] arguments that will be forwarded to the function
* @returns {Promise} promise for the return value of func
*/
function call(func /*, args... */) {
return _apply(func, this, slice.call(arguments, 1));
}
/**
* Takes a 'regular' function and returns a version of that function that
* returns a promise instead of a plain value, and handles thrown errors by
* returning a rejected promise. Also accepts a list of arguments to be
* prepended to the new function, as does Function.prototype.bind.
*
* The resulting function is promise-aware, in the sense that it accepts
* promise arguments, and waits for their resolution.
*
* @example
* function mayThrowError(n) {
* if(n % 2 === 1) { // Normally this wouldn't be so deterministic :)
* throw new Error("I don't like odd numbers");
* } else {
* return n;
* }
* }
*
* var lifted = fn.lift(mayThrowError);
*
* // Logs "I don't like odd numbers"
* lifted(1).then(console.log, console.error);
*
* // Logs '6'
* lifted(6).then(console.log, console.error);
*
* @example
* function sumTwoNumbers(x, y) {
* return x + y;
* }
*
* var sumWithFive = fn.lifted(sumTwoNumbers, 5);
*
* // Logs '15'
* sumWithFive(10).then(console.log, console.error);
*
* @param {Function} func function to be bound
* @param {...*} [args] arguments to be prepended for the new function
* @returns {Function} a promise-returning function
*/
function lift(func /*, args... */) {
var args = slice.call(arguments, 1);
return function() {
return _apply(func, this, args.concat(slice.call(arguments)));
};
}
/**
* Composes multiple functions by piping their return values. It is
* transparent to whether the functions return 'regular' values or promises:
* the piped argument is always a resolved value. If one of the functions
* throws or returns a rejected promise, the composed promise will be also
* rejected.
*
* The arguments (or promises to arguments) given to the returned function (if
* any), are passed directly to the first function on the 'pipeline'.
*
* @example
* function getHowMuchWeWillDestroy(parameter) {
* // Makes some calculations to find out which items the modification the user
* // wants will destroy. Returns a number
* }
*
* function getUserConfirmation(itemsCount) {
* // Return a resolved promise if the user confirms the destruction,
* // and rejects it otherwise
* }
*
* function saveModifications() {
* // Makes ajax to save modifications on the server, returning a
* // promise.
* }
*
* function showNotification() {
* // Notifies that the modification was successful
* }
*
* // Composes the whole process into one function that returns a promise
* var wholeProcess = func.compose(getHowMuchWeWillDestroy,
* getUserConfirmation,
* saveModifications,
* showNotification);
*
* // Which is equivalent to
* var wholeProcess = function(parameter) {
* return fn.call(getHowMuchWeWillDestroy, parameter)
* .then(getUserConfirmation)
* .then(saveModifications)
* .then(showNotification);
* }
*
* @param {Function} f the function to which the arguments will be passed
* @param {...Function} [funcs] functions that will be composed, in order
* @returns {Function} a promise-returning composition of the functions
*/
function compose(f /*, funcs... */) {
var funcs = slice.call(arguments, 1);
return function() {
var thisArg, args, firstPromise;
thisArg = this;
args = slice.call(arguments);
firstPromise = _apply(f, thisArg, args);
return when.reduce(funcs, function(arg, func) {
return func.call(thisArg, arg);
}, firstPromise);
};
}
});
})(
typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }
// Boilerplate for AMD and Node
);