Permalink
Browse files

Initial commit

  • Loading branch information...
tj committed Jun 6, 2013
0 parents commit 7a6e73335a8623b145fd67dd79eed348021c8fe5
Showing with 769 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +10 −0 Makefile
  3. +253 −0 Readme.md
  4. +44 −0 examples/join.js
  5. +33 −0 examples/nested.js
  6. +37 −0 examples/requests.js
  7. +16 −0 examples/return.js
  8. +18 −0 examples/simple.js
  9. +14 −0 examples/wrap.js
  10. +107 −0 index.js
  11. +11 −0 package.json
  12. +165 −0 test/index.js
  13. +42 −0 test/join.js
  14. +17 −0 test/wrap.js
@@ -0,0 +1,2 @@
+node_modules
+test.js
@@ -0,0 +1,10 @@
+
+test:
+ @./node_modules/.bin/mocha \
+ --require should \
+ --reporter spec \
+ --slow 2s \
+ --harmony \
+ --bail
+
+.PHONY: test
253 Readme.md
@@ -0,0 +1,253 @@
+
+# Co
+
+ Generator based flow-control goodness for nodejs (and soon the browser), using
+ thunks instead of promises.
+
+## Installation
+
+```
+$ npm install co
+```
+
+## Example
+
+```js
+var co = require('co');
+
+co(function *(){
+ var a = yield get('http://google.com');
+ var b = yield get('http://yahoo.com');
+ var c = yield get('http://cloudup.com');
+ console.log(a.status);
+ console.log(b.status);
+ console.log(c.status);
+})
+```
+
+## Why not promises
+
+ I'm not a fan of promises personally, but preferences aside this
+ is an extremely minimal layer between the traditional node `(err, result)`
+ style callback using thunks. All you need to do is return a function, this
+ function behaves as the "thunk" or "future" that is `yield`ed to Co.
+
+ For example take `fs.readFile`, we all know the signature is:
+
+```js
+fs.readFile(path, encoding, function(err, result){
+
+});
+```
+
+ To work with Co we need a function to return another function of
+ the same signature:
+
+```js
+fs.readFile(path, encoding)(function(err, result){
+
+});
+```
+
+ Which basically looks like this:
+
+```js
+function read(path, encoding) {
+ return function(cb){
+ fs.readFile(path, encoding, cb);
+ }
+}
+```
+
+ This is what the `co.wrap(fn)` utility function does for you.
+
+## API
+
+### co(fn)
+
+ Pass a generator `fn` which is immediately invoked. Any `yield` expressions
+ within _must_ return a "thunk", at which point `co()` will defer execution.
+
+```js
+var co = require('co');
+var fs = require('fs');
+
+function read(file) {
+ return function(fn){
+ fs.readFile(file, 'utf8', fn);
+ }
+}
+
+co(function *(){
+ var a = yield read('.gitignore');
+ var b = yield read('Makefile');
+ var c = yield read('package.json');
+ console.log(a);
+ console.log(b);
+ console.log(c);
+});
+```
+
+#### Nesting co() calls
+
+ The `co()` function itself returns a thunk, this means you can nest appropriately:
+
+```js
+var co = require('co');
+var fs = require('fs');
+
+function size(file) {
+ return function(fn){
+ fs.stat(file, function(err, stat){
+ if (err) return fn(err);
+ fn(null, stat.size);
+ });
+ }
+}
+
+var foo = co(function *(){
+ var a = yield size('.gitignore');
+ var b = yield size('Makefile');
+ var c = yield size('package.json');
+ return [a, b, c];
+})
+
+var bar = co(function *(){
+ var a = yield size('examples/parallel.js');
+ var b = yield size('examples/nested.js');
+ var c = yield size('examples/simple.js');
+ return [a, b, c];
+})
+
+co(function *(){
+ var a = yield foo;
+ var b = yield bar;
+ console.log(a);
+ console.log(b);
+});
+```
+
+ Or a variation of the same thing:
+
+```js
+var co = require('co');
+var fs = require('fs');
+
+function size(file) {
+ return function(fn){
+ fs.stat(file, function(err, stat){
+ if (err) return fn(err);
+ fn(null, stat.size);
+ });
+ }
+}
+
+co(function *(){
+ var a = yield co(function *(){
+ var a = yield size('.gitignore');
+ var b = yield size('Makefile');
+ var c = yield size('package.json');
+ return [a, b, c];
+ })
+
+ var b = yield co(function *(){
+ var a = yield size('examples/parallel.js');
+ var b = yield size('examples/nested.js');
+ var c = yield size('examples/simple.js');
+ return [a, b, c];
+ })
+
+ console.log(a);
+ console.log(b);
+});
+```
+
+#### co() return values
+
+ Since `co()` returns a thunk, you may pass a function to this thunk
+ to receive the `return` values from the generator. Any error that occurs
+ is passed to this (`sizes`) function.
+
+```js
+
+var co = require('..');
+var fs = require('fs');
+
+var read = co.wrap(fs.readFile);
+
+var sizes = co(function *(){
+ var a = yield read('.gitignore');
+ var b = yield read('Makefile');
+ var c = yield read('package.json');
+ return [a.length, b.length, c.length];
+});
+
+sizes(function(err, res){
+ console.log(res);
+});
+```
+
+### co.wrap(fn)
+
+ The `co.wrap()` utility simply wraps a node-style function to return a thunk.
+
+```js
+
+var co = require('..');
+var fs = require('fs');
+
+var read = co.wrap(fs.readFile);
+
+co(function *(){
+ var a = yield read('.gitignore');
+ var b = yield read('Makefile', 'ascii');
+ var c = yield read('package.json', 'utf8');
+ console.log(a);
+ console.log(b);
+ console.log(c);
+});
+```
+
+### co.join(fn...)
+
+ The `co.join()` utility function allows you to pass multiple thunks, or an array
+ of thunks and "join" them all into a single thunk which executes them all concurrently,
+ instead of in sequence. Note that the resulting array ordering _is_ retained.
+
+```js
+
+var co = require('..');
+var join = co.join;
+var fs = require('fs');
+
+function size(file) {
+ return function(fn){
+ fs.stat(file, function(err, stat){
+ if (err) return fn(err);
+ fn(null, stat.size);
+ });
+ }
+}
+
+co(function *(){
+ var a = size('.gitignore');
+ var b = size('index.js');
+ var c = size('Makefile');
+ var res = yield join(a, b, c);
+ console.log(res);
+ // => [ 13, 1687, 129 ]
+});
+```
+
+## FAQ
+
+### How does this compare to taskjs?
+
+ - it's smaller
+ - it's not a scheduler
+ - it doesn't use promises
+
+## License
+
+ MIT
+
@@ -0,0 +1,44 @@
+
+var co = require('..');
+var join = co.join;
+var fs = require('fs');
+
+function size(file) {
+ return function(fn){
+ fs.stat(file, function(err, stat){
+ if (err) return fn(err);
+ fn(null, stat.size);
+ });
+ }
+}
+
+// 3 concurrent stat()s at a time
+
+co(function *(){
+ var a = yield join(size('.gitignore'), size('index.js'), size('Makefile'));
+ var b = yield join(size('.gitignore'), size('index.js'), size('Makefile'));
+ var c = yield join(size('.gitignore'), size('index.js'), size('Makefile'));
+ console.log(a);
+ console.log(b);
+ console.log(c);
+});
+
+// 9 concurrent stat()s
+
+co(function *(){
+ var a = join(size('.gitignore'), size('index.js'), size('Makefile'));
+ var b = join(size('.gitignore'), size('index.js'), size('Makefile'));
+ var c = join(size('.gitignore'), size('index.js'), size('Makefile'));
+ var d = yield join(a, b, c);
+ console.log(d);
+});
+
+// 3
+
+co(function *(){
+ var a = size('.gitignore');
+ var b = size('index.js');
+ var c = size('Makefile');
+ var res = yield join(a, b, c);
+ console.log(res);
+});
@@ -0,0 +1,33 @@
+
+var co = require('..');
+var fs = require('fs');
+
+function size(file) {
+ return function(fn){
+ fs.stat(file, function(err, stat){
+ if (err) return fn(err);
+ fn(null, stat.size);
+ });
+ }
+}
+
+var foo = co(function *(){
+ var a = yield size('.gitignore');
+ var b = yield size('Makefile');
+ var c = yield size('package.json');
+ return [a, b, c];
+})
+
+var bar = co(function *(){
+ var a = yield size('examples/parallel.js');
+ var b = yield size('examples/nested.js');
+ var c = yield size('examples/simple.js');
+ return [a, b, c];
+})
+
+co(function *(){
+ var a = yield foo;
+ var b = yield bar;
+ console.log(a);
+ console.log(b);
+})
@@ -0,0 +1,37 @@
+
+var co = require('..');
+var join = co.join;
+var request = require('superagent');
+
+var get = co.wrap(request.get);
+
+var urls = [
+ 'http://google.com',
+ 'http://yahoo.com',
+ 'http://cloudup.com',
+ 'http://ign.com'
+];
+
+// sequential
+
+co(function *(){
+ for (var i = 0; i < urls.length; i++) {
+ var url = urls[i];
+ var res = yield get(url);
+ console.log('%s -> %s', url, res.status);
+ }
+})
+
+// parallel
+
+co(function *(){
+ var requests = urls.map(function(url){
+ return get(url);
+ });
+
+ var responses = yield join(requests);
+
+ console.log(responses.map(function(r){
+ return r.status;
+ }));
+})
Oops, something went wrong.

5 comments on commit 7a6e733

When you crazy programmers create awesome tools, do you begin by scaffolding a project?

And I am quite surprised that you created tests... in the first commit.

Owner

tj replied Jul 5, 2017

I typically don't open source something until it's basically complete, or at least usable. Then I rm -fr .git and commit all of that as the starting point, unless I've been keeping a clean commit log haha, but that's rare when I'm trying to figure out an API etc.

Thank you. You have no idea how much respect the work you have done in the community! Hollering at you man. thank you for the inspiration

Owner

tj replied Jul 6, 2017

Thanks man! :D

Please sign in to comment.