UNPKG

3.94 kBJavaScriptView Raw
1
2/**
3 * toString reference.
4 */
5
6var toString = Object.prototype.toString;
7
8/**
9 * Expose `co`.
10 */
11
12exports = module.exports = co;
13
14/**
15 * Wrap the given generator `fn` with
16 * optional `done` callback.
17 *
18 * @param {Function} fn
19 * @param {Function} [done]
20 * @return {Function}
21 * @api public
22 */
23
24function co(fn, done, ctx) {
25 var gen = isGenerator(fn) ? fn : fn.call(this);
26 ctx = ctx || this;
27
28 function next(err, res) {
29 var ret;
30
31 // multiple args
32 if (arguments.length > 2) {
33 res = [].slice.call(arguments, 1);
34 }
35
36 // error
37 if (err) {
38 try {
39 ret = gen.throw(err);
40 } catch (e) {
41 if (!done) throw e;
42 return done(e);
43 }
44 }
45
46 // ok
47 if (!err) {
48 try {
49 ret = gen.next(res);
50 } catch (e) {
51 if (!done) throw e;
52 return done(e);
53 }
54 }
55
56 // done
57 if (ret.done) {
58 if (done) done(null, ret.value);
59 return;
60 }
61
62 // normalize
63 ret.value = toThunk(ret.value, ctx);
64
65 // run
66 if ('function' == typeof ret.value) {
67 try {
68 ret.value.call(ctx, next);
69 } catch (e) {
70 setImmediate(function(){
71 next(e);
72 });
73 }
74 return;
75 }
76
77 // invalid
78 next(new Error('yield a function, promise, generator, or array'));
79 }
80
81 if (done) next();
82 else setImmediate(next);
83
84 return function(fn){
85 done = fn;
86 }
87}
88
89/**
90 * Wrap regular callback style `fn` as a thunk.
91 *
92 * @param {Function} fn
93 * @return {Function}
94 * @api public
95 */
96
97exports.wrap = function(fn, ctx){
98 return function(){
99 var args = [].slice.call(arguments);
100 return function(done){
101 args.push(done);
102 fn.apply(ctx || this, args);
103 }
104 }
105};
106
107/**
108 * Join the given `fns`.
109 *
110 * @param {Array|Function} ...
111 * @return {Function}
112 * @api public
113 */
114
115exports.join = function(fns) {
116 if (!Array.isArray(fns)) fns = [].slice.call(arguments);
117 var ctx = this;
118
119 return function(done){
120 var pending = fns.length;
121 var results = new Array(pending);
122 var finished;
123
124 if (!pending) {
125 setImmediate(function(){
126 done(null, results);
127 });
128 return;
129 }
130
131 for (var i = 0; i < fns.length; i++) {
132 run(fns[i], i);
133 }
134
135 function run(fn, i) {
136 if (finished) return;
137 try {
138 fn = toThunk(fn, ctx);
139
140 fn.call(ctx, function(err, res){
141 if (finished) return;
142
143 if (err) {
144 finished = true;
145 return done(err);
146 }
147
148 results[i] = res;
149 --pending || done(null, results);
150 });
151 } catch (err) {
152 finished = true;
153 done(err);
154 }
155 }
156 }
157};
158
159/**
160 * Convert `obj` into a normalized thunk.
161 *
162 * @param {Mixed} obj
163 * @param {Mixed} ctx
164 * @return {Function}
165 * @api private
166 */
167
168function toThunk(obj, ctx) {
169 var fn = obj;
170 if (Array.isArray(obj)) fn = exports.join.call(ctx, obj);
171 if (isGeneratorFunction(obj)) obj = obj.call(ctx);
172 if (isGenerator(obj)) fn = function(done){ co(obj, done, ctx) };
173 if (isPromise(obj)) fn = promiseToThunk(obj);
174 return fn;
175}
176
177/**
178 * Convert `promise` to a thunk.
179 *
180 * @param {Object} promise
181 * @return {Function}
182 * @api private
183 */
184
185function promiseToThunk(promise) {
186 return function(fn){
187 promise.then(function(res) {
188 fn(null, res);
189 }, fn);
190 }
191}
192
193/**
194 * Check if `obj` is a promise.
195 *
196 * @param {Object} obj
197 * @return {Boolean}
198 * @api private
199 */
200
201function isPromise(obj) {
202 return obj && 'function' == typeof obj.then;
203}
204
205/**
206 * Check if `fn` is a generator.
207 *
208 * @param {Mixed} obj
209 * @return {Boolean}
210 * @api private
211 */
212
213function isGenerator(obj) {
214 return obj && '[object Generator]' == toString.call(obj);
215}
216
217/**
218 * Check if `fn` is a generator function.
219 *
220 * @param {Mixed} obj
221 * @return {Boolean}
222 * @api private
223 */
224
225function isGeneratorFunction(obj) {
226 return obj && obj.constructor && 'GeneratorFunction' == obj.constructor.name;
227}