-
Notifications
You must be signed in to change notification settings - Fork 0
/
thunkify.js
128 lines (107 loc) · 3.05 KB
/
thunkify.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
// thunkify.js
(function () {
'use strict';
// thunkify(fn) or
// thunkify(ctx, fn) or
// thunkify.call(ctx, fn)
function thunkify(ctx, fn) {
// (fn)
if (typeof fn !== 'function')
fn = ctx, ctx = this;
// (ctx, fn)
if (typeof fn !== 'function')
throw new TypeError('argument must be a function');
// return thunk
return function thunk() {
var callbacks = new Callbacks(ctx);
arguments[arguments.length++] = function callback() {
callbacks.callback(arguments);
};
fn.apply(ctx, arguments);
// return yieldable promise
return callbacks.callback;
}; // thunk
} // thunkify
exports = module.exports = thunkify;
// class: Callbacks
// method: callback(arguments/callback function) for yieldable/promise
// usage:
// var callbacks = new Callbacks(this);
//
// function callback() {
// callbacks.callback(arguments);
// }
//
// // return yieldable
// return callbacks.callback;
function Callbacks(ctx) {
var results, callbacks = [];
this.callback = callback;
callback.then = then;
callback.done = then;
callback.error = error;
callback['catch'] = error;
// yieldable/callback callbacks
function callback(args) {
// push callback function
if (typeof args === 'function') {
callbacks.push({fn: args, called: false});
}
// set arguments object for callback results
else if (typeof args === 'object') {
if (!results) results = args;
}
else {
throw new Error('invalid arguments for callback (Callbacks) type: ' + typeof args);
}
// not unsolved
if (!results) return;
callbacks.forEach(function (callback) {
if (callback.called) return;
callback.called = true;
callback.fn.apply(ctx, results);
}); // callbacks.forEach
} // callback
// Promise then/done
function then(resolved, rejected) {
var cb = new Callbacks(ctx);
callback(function (err, data) {
var res;
if (err) {
var args = arguments;
function errArgs() { cb.callback(args); }
if (rejected) res = rejected(err);
if (isPromise(res))
return res.then(errArgs, errArgs);
if (typeof res === 'function') // isThunk
return res(errArgs);
return cb.callback(args);
}
else {
function errThru() { cb.callback(arguments); }
if (resolved) res = resolved(data);
if (isPromise(res)) {
return res.then(
function (res) { cb.callback([null, res]); },
errThru
);
} // isPromise
if (typeof res === 'function') // isThunk
return res(errThru);
if (res === undefined) res = data;
return cb.callback([null, res]);
}
}); // callback
return cb.callback; // return yieldable promise
} // then/done
// Promise catch/error
function error(rejected) {
return then(undefined, rejected);
} // catch/error
}
exports.Callbacks = Callbacks;
function isPromise(promise) {
return !!promise && typeof promise.then === 'function';
}
// TODO: NYI(Not Yet Implemented) Promise all race
})();