UNPKG

7.46 kBMarkdownView Raw
1# Co
2
3 Generator based flow-control goodness for nodejs (and soon the browser), using
4 thunks _or_ promises, letting you write non-blocking code in a nice-ish
5 way.
6
7 Currently you must use the `--harmony-generators` flag when
8 running node 0.11.x to get access to generators.
9
10 Co is careful to relay any errors that occur back to the generator, including those
11 within the thunk, or from the thunk's callback. "Uncaught" exceptions in the generator are
12 then either passed `co()`'s thunk or thrown.
13
14 Make sure to view the [examples](https://github.com/visionmedia/co/tree/master/examples).
15
16## Installation
17
18```
19$ npm install co
20```
21
22## Associated libraries
23
24 - [co-fs](https://github.com/visionmedia/co-fs) - core `fs` function wrappers
25 - [co-exec](https://github.com/visionmedia/co-exec) - core `exec` function wrapper
26 - [co-prompt](https://github.com/visionmedia/co-prompt) - terminal user input utilities
27 - [co-express](https://github.com/mciparelli/co-express) - [express](https://github.com/visionmedia/express) wrapper that enables generators to be used as middlewares
28 - [level-co](https://github.com/juliangruber/level-co) - levelup wrapper
29
30## Example
31
32```js
33var co = require('co');
34
35co(function *(){
36 var a = yield get('http://google.com');
37 var b = yield get('http://yahoo.com');
38 var c = yield get('http://cloudup.com');
39 console.log(a.status);
40 console.log(b.status);
41 console.log(c.status);
42})
43
44co(function *(){
45 var a = get('http://google.com');
46 var b = get('http://yahoo.com');
47 var c = get('http://cloudup.com');
48 var res = yield [a, b, c];
49 console.log(res);
50})
51```
52
53## Yieldables
54
55 The "yieldable" objects currently supported are:
56
57 - promises
58 - thunks (functions)
59 - array (parallel execution)
60 - generators (delegation)
61 - generator functions (delegation)
62
63## Thunks vs promises
64
65 While co supports promises, you may return "thunks" from your functions,
66 which otherwise behaves just like the traditional node-style callback
67 with a signature of: `(err, result)`.
68
69
70 For example take `fs.readFile`, we all know the signature is:
71
72```js
73fs.readFile(path, encoding, function(err, result){
74
75});
76```
77
78 To work with Co we need a function to return another function of
79 the same signature:
80
81```js
82fs.readFile(path, encoding)(function(err, result){
83
84});
85```
86
87 Which basically looks like this:
88
89```js
90function read(path, encoding) {
91 return function(cb){
92 fs.readFile(path, encoding, cb);
93 }
94}
95```
96
97 This is what the `co.wrap(fn)` utility function does for you.
98
99## Receiver propagation
100
101 When `co` is invoked with a receiver it will propagate to most yieldables,
102 allowing you to alter `this`.
103
104```js
105var ctx = {};
106
107function foo() {
108 assert(this == ctx);
109}
110
111co.call(ctx, function *(){
112 assert(this == ctx);
113 yield foo;
114});
115```
116
117## API
118
119### co(fn)
120
121 Pass a generator `fn` which is immediately invoked. Any `yield` expressions
122 within _must_ return a "thunk", at which point `co()` will defer execution.
123
124```js
125var co = require('co');
126var fs = require('fs');
127
128function read(file) {
129 return function(fn){
130 fs.readFile(file, 'utf8', fn);
131 }
132}
133
134co(function *(){
135 var a = yield read('.gitignore');
136 var b = yield read('Makefile');
137 var c = yield read('package.json');
138 console.log(a);
139 console.log(b);
140 console.log(c);
141});
142```
143
144 You may also yield `Generator` objects to support nesting:
145
146
147```js
148var co = require('co');
149var fs = require('fs');
150
151function size(file) {
152 return function(fn){
153 fs.stat(file, function(err, stat){
154 if (err) return fn(err);
155 fn(null, stat.size);
156 });
157 }
158}
159
160function *foo(){
161 var a = yield size('.gitignore');
162 var b = yield size('Makefile');
163 var c = yield size('package.json');
164 return [a, b, c];
165}
166
167function *bar(){
168 var a = yield size('examples/parallel.js');
169 var b = yield size('examples/nested.js');
170 var c = yield size('examples/simple.js');
171 return [a, b, c];
172}
173
174co(function *(){
175 var a = yield foo();
176 var b = yield bar();
177 console.log(a);
178 console.log(b);
179});
180```
181
182 Or if the generator functions do not require arguments, simply `yield` the function:
183
184```js
185var request = require('superagent');
186
187var get = co.wrap(request.get);
188
189function *results() {
190 var a = yield get('http://google.com')
191 var b = yield get('http://yahoo.com')
192 var c = yield get('http://ign.com')
193 return [a.status, b.status, c.status]
194}
195
196co(function *(){
197 // 3 concurrent requests at a time
198 var a = yield results;
199 var b = yield results;
200 var c = yield results;
201 console.log(a, b, c);
202
203 // 9 concurrent requests
204 console.log(yield [results, results, results]);
205});
206```
207
208#### co() return values
209
210 Since `co()` returns a thunk, you may pass a function to this thunk
211 to receive the `return` values from the generator. Any error that occurs
212 is passed to this (`sizes`) function.
213
214```js
215
216var co = require('co');
217var fs = require('fs');
218
219var read = co.wrap(fs.readFile);
220
221var sizes = co(function *(){
222 var a = yield read('.gitignore');
223 var b = yield read('Makefile');
224 var c = yield read('package.json');
225 return [a.length, b.length, c.length];
226});
227
228sizes(function(err, res){
229 console.log(res);
230});
231```
232
233### co.wrap(fn, [ctx])
234
235 The `co.wrap()` utility simply wraps a node-style function to return a thunk.
236
237```js
238
239var co = require('co');
240var fs = require('fs');
241
242var read = co.wrap(fs.readFile);
243
244co(function *(){
245 var a = yield read('.gitignore');
246 var b = yield read('Makefile', 'ascii');
247 var c = yield read('package.json', 'utf8');
248 console.log(a);
249 console.log(b);
250 console.log(c);
251});
252```
253
254Optionally you may pass the `fn`'s receiver as the `ctx` as shown here:
255
256```js
257
258var co = require('co')
259var redis = require('redis')
260var db = redis.createClient()
261
262db.set = co.wrap(db.set, db)
263db.get = co.wrap(db.get, db)
264
265co(function *(){
266 yield db.set('foo', 'bar')
267 yield db.set('bar', 'baz')
268
269 var res = yield db.get('foo')
270 console.log('foo -> %s', res);
271
272 var res = yield db.get('bar')
273 console.log('bar -> %s', res);
274})
275```
276
277### co.join(fn...)
278
279 The `co.join()` utility function allows you to pass multiple thunks, or an array
280 of thunks and "join" them all into a single thunk which executes them all concurrently,
281 instead of in sequence. Note that the resulting array ordering _is_ retained.
282
283```js
284
285var co = require('co');
286var join = co.join;
287var fs = require('fs');
288
289function size(file) {
290 return function(fn){
291 fs.stat(file, function(err, stat){
292 if (err) return fn(err);
293 fn(null, stat.size);
294 });
295 }
296}
297
298co(function *(){
299 var a = size('.gitignore');
300 var b = size('index.js');
301 var c = size('Makefile');
302 var res = yield join(a, b, c);
303 console.log(res);
304 // => [ 13, 1687, 129 ]
305});
306```
307
308 As an alias of `join(array)` you may simply `yield` an array:
309
310```js
311co(function *(){
312 var a = size('.gitignore');
313 var b = size('index.js');
314 var c = size('Makefile');
315 var res = yield [a, b, c];
316 console.log(res);
317 // => [ 13, 1687, 129 ]
318});
319```
320
321 Nested joins may also be expressed as simple nested arrays:
322
323```js
324var a = [
325 get('http://google.com'),
326 get('http://yahoo.com'),
327 get('http://ign.com')
328];
329
330var b = [
331 get('http://google.com'),
332 get('http://yahoo.com'),
333 get('http://ign.com')
334];
335
336console.log(yield [a, b]);
337```
338
339### Performance
340
341 On my machine 30,000 sequential stat()s takes an avg of 570ms,
342 while the same number of sequential stat()s with `co()` takes
343 610ms, aka the overhead introduced by generators is _extremely_ negligable.
344
345## License
346
347 MIT
348