1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | var toString = Object.prototype.toString;
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | exports = module.exports = co;
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 | function 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 |
|
32 | if (arguments.length > 2) {
|
33 | res = [].slice.call(arguments, 1);
|
34 | }
|
35 |
|
36 |
|
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 |
|
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 |
|
57 | if (ret.done) {
|
58 | if (done) done(null, ret.value);
|
59 | return;
|
60 | }
|
61 |
|
62 |
|
63 | ret.value = toThunk(ret.value, ctx);
|
64 |
|
65 |
|
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 |
|
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 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 | exports.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 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 | exports.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 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 | function 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 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 | function promiseToThunk(promise) {
|
186 | return function(fn){
|
187 | promise.then(function(res) {
|
188 | fn(null, res);
|
189 | }, fn);
|
190 | }
|
191 | }
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 | function isPromise(obj) {
|
202 | return obj && 'function' == typeof obj.then;
|
203 | }
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 | function isGenerator(obj) {
|
214 | return obj && '[object Generator]' == toString.call(obj);
|
215 | }
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 |
|
223 |
|
224 |
|
225 | function isGeneratorFunction(obj) {
|
226 | return obj && obj.constructor && 'GeneratorFunction' == obj.constructor.name;
|
227 | }
|