Comparing version 2.3.0 to 3.0.0
@@ -0,8 +1,14 @@ | ||
3.0.0 / 2013-12-19 | ||
================== | ||
2.3.0 / 2013-11-12 | ||
* change: `co(function *(){})` now returns a reusable thunk | ||
* change: `this` must now be passed through the returned thunk, ex. `co(function *(){}).call(this)` | ||
* fix "generator already finished" errors | ||
2.3.0 / 2013-11-12 | ||
================== | ||
* add `yield object` support | ||
* add `yield object` support | ||
2.2.0 / 2013-11-05 | ||
2.2.0 / 2013-11-05 | ||
================== | ||
@@ -12,3 +18,3 @@ | ||
2.1.0 / 2013-10-21 | ||
2.1.0 / 2013-10-21 | ||
================== | ||
@@ -18,3 +24,3 @@ | ||
2.0.0 / 2013-10-14 | ||
2.0.0 / 2013-10-14 | ||
================== | ||
@@ -25,3 +31,3 @@ | ||
1.5.2 / 2013-09-02 | ||
1.5.2 / 2013-09-02 | ||
================== | ||
@@ -31,3 +37,3 @@ | ||
1.5.1 / 2013-08-11 | ||
1.5.1 / 2013-08-11 | ||
================== | ||
@@ -37,3 +43,3 @@ | ||
0.5.0 / 2013-08-10 | ||
0.5.0 / 2013-08-10 | ||
================== | ||
@@ -44,3 +50,3 @@ | ||
1.4.1 / 2013-07-01 | ||
1.4.1 / 2013-07-01 | ||
================== | ||
@@ -50,3 +56,3 @@ | ||
1.4.0 / 2013-06-21 | ||
1.4.0 / 2013-06-21 | ||
================== | ||
@@ -59,3 +65,3 @@ | ||
1.3.0 / 2013-06-10 | ||
1.3.0 / 2013-06-10 | ||
================== | ||
@@ -65,3 +71,3 @@ | ||
1.2.1 / 2013-06-08 | ||
1.2.1 / 2013-06-08 | ||
================== | ||
@@ -71,3 +77,3 @@ | ||
1.2.0 / 2013-06-08 | ||
1.2.0 / 2013-06-08 | ||
================== | ||
@@ -77,3 +83,3 @@ | ||
1.1.0 / 2013-06-06 | ||
1.1.0 / 2013-06-06 | ||
================== | ||
@@ -80,0 +86,0 @@ |
146
index.js
@@ -1,2 +0,1 @@ | ||
/** | ||
@@ -30,65 +29,85 @@ * toString() reference. | ||
function co(fn) { | ||
var ctx = this; | ||
var done; | ||
var gen; | ||
var isGenFun = isGeneratorFunction(fn); | ||
function next(err, res) { | ||
var ret; | ||
return function (done) { | ||
var ctx = this; | ||
// multiple args | ||
if (arguments.length > 2) { | ||
res = slice.call(arguments, 1); | ||
// in toThink() below we invoke co() | ||
// with a generator, so optimize for | ||
// this case | ||
var gen = fn; | ||
done = done || error; | ||
if (isGenFun) { | ||
// we only need to parse the arguments | ||
// if gen is a generator function. | ||
var args = slice.call(arguments); | ||
// no callback provided | ||
if (!args.length) done = error; | ||
// done is already the callback | ||
else if (1 == args.length && 'function' == typeof done) ; | ||
// callback is the last argument | ||
else if ('function' == typeof args[args.length - 1]) done = args.pop(); | ||
// arguments provided, but no callbacks | ||
else done = error; | ||
gen = fn.apply(this, args); | ||
} | ||
// error | ||
if (err) { | ||
try { | ||
ret = gen.throw(err); | ||
} catch (e) { | ||
if (!done) throw e; | ||
return done(e); | ||
next(); | ||
function next(err, res) { | ||
var ret; | ||
// multiple args | ||
if (arguments.length > 2) res = slice.call(arguments, 1); | ||
// error | ||
if (err) { | ||
try { | ||
ret = gen.throw(err); | ||
} catch (e) { | ||
return done(e); | ||
} | ||
} | ||
} | ||
// ok | ||
if (!err) { | ||
try { | ||
ret = gen.next(res); | ||
} catch (e) { | ||
if (!done) throw e; | ||
return done(e); | ||
// ok | ||
if (!err) { | ||
try { | ||
ret = gen.next(res); | ||
} catch (e) { | ||
return done(e); | ||
} | ||
} | ||
} | ||
// done | ||
if (ret.done) { | ||
if (done) done(null, ret.value); | ||
return; | ||
} | ||
// done | ||
if (ret.done) return done(null, ret.value); | ||
// normalize | ||
ret.value = toThunk(ret.value, ctx); | ||
// normalize | ||
ret.value = toThunk(ret.value, ctx); | ||
// run | ||
if ('function' == typeof ret.value) { | ||
try { | ||
ret.value.call(ctx, next); | ||
} catch (e) { | ||
setImmediate(function(){ | ||
next(e); | ||
}); | ||
// run | ||
if ('function' == typeof ret.value) { | ||
var called = false; | ||
try { | ||
ret.value.call(ctx, function(){ | ||
if (called) return; | ||
called = true; | ||
next.apply(ctx, arguments); | ||
}); | ||
} catch (e) { | ||
setImmediate(function(){ | ||
if (called) return; | ||
called = true; | ||
next(e); | ||
}); | ||
} | ||
return; | ||
} | ||
return; | ||
// invalid | ||
next(new Error('yield a function, promise, generator, array, or object')); | ||
} | ||
// invalid | ||
next(new Error('yield a function, promise, generator, array, or object')); | ||
} | ||
return function(){ | ||
var args = slice.call(arguments); | ||
done = args.pop(); | ||
gen = isGenerator(fn) ? fn : fn.apply(ctx, args); | ||
next(); | ||
} | ||
} | ||
@@ -199,2 +218,7 @@ | ||
if ('function' != typeof fn) { | ||
results[key] = fn; | ||
return --pending || done(null, results); | ||
} | ||
fn.call(ctx, function(err, res){ | ||
@@ -270,1 +294,21 @@ if (finished) return; | ||
} | ||
/** | ||
* Throw `err` in a new stack. | ||
* | ||
* This is used when co() is invoked | ||
* without supplying a callback, which | ||
* should only be for demonstrational | ||
* purposes. | ||
* | ||
* @param {Error} err | ||
* @api private | ||
*/ | ||
function error(err) { | ||
if (!err) return; | ||
setImmediate(function(){ | ||
throw err; | ||
}); | ||
} |
{ | ||
"name": "co", | ||
"version": "2.3.0", | ||
"version": "3.0.0", | ||
"description": "generator async flow control goodness", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
# Co | ||
[![Build Status](https://travis-ci.org/visionmedia/co.png)](https://travis-ci.org/visionmedia/co) | ||
Generator based flow-control goodness for nodejs (and soon the browser), using | ||
@@ -57,7 +59,7 @@ thunks _or_ promises, letting you write non-blocking code in a nice-ish | ||
- array (parallel execution) | ||
- objects (parallel execution) | ||
- generators (delegation) | ||
- generator functions (delegation) | ||
To convert a regular node function that accepts a callback | ||
into one which returns a thunk you may want to use [thunkify](https://github.com/visionmedia/node-thunkify) or similar. | ||
To convert a regular node function that accepts a callback into one which returns a thunk you may want to use [thunkify](https://github.com/visionmedia/node-thunkify) or similar. | ||
@@ -110,6 +112,6 @@ ## Thunks vs promises | ||
co.call(ctx, function *(){ | ||
co(function *(){ | ||
assert(this == ctx); | ||
yield foo; | ||
})() | ||
}).call(ctx) | ||
``` | ||
@@ -120,7 +122,7 @@ | ||
```js | ||
co.call(ctx, function *(a){ | ||
co(function *(a){ | ||
assert(this == ctx); | ||
assert('yay' == a); | ||
yield foo; | ||
})('yay'); | ||
}).call(ctx, 'yay'); | ||
``` | ||
@@ -256,6 +258,5 @@ | ||
### co.join(fn...) | ||
### yield array | ||
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, | ||
By yielding an array of thunks you may "join" them all into a single thunk which executes them all concurrently, | ||
instead of in sequence. Note that the resulting array ordering _is_ retained. | ||
@@ -285,3 +286,3 @@ | ||
// => [ 13, 1687, 129 ] | ||
}); | ||
})() | ||
``` | ||
@@ -299,3 +300,3 @@ | ||
// => [ 13, 1687, 129 ] | ||
}); | ||
})() | ||
``` | ||
@@ -321,2 +322,30 @@ | ||
### yield object | ||
Yielding an object behaves much like yielding an array, however recursion is supported: | ||
```js | ||
co(function *(){ | ||
var user = yield { | ||
name: { | ||
first: get('name.first'), | ||
last: get('name.last') | ||
} | ||
}; | ||
})() | ||
``` | ||
Here is the sequential equivalent without yielding an object: | ||
```js | ||
co(function *(){ | ||
var user = { | ||
name: { | ||
first: yield get('name.first'), | ||
last: yield get('name.last') | ||
} | ||
}; | ||
})() | ||
``` | ||
### Performance | ||
@@ -323,0 +352,0 @@ |
@@ -32,18 +32,3 @@ | ||
}) | ||
it('should pass arguments into generator', function(done) { | ||
co(function *(a, b) { | ||
assert('yay' == a); | ||
assert('wahoo' == b); | ||
})('yay', 'wahoo', done); | ||
}); | ||
it('should pass arguments into generator with yields', function(done) { | ||
co(function *(a, b) { | ||
assert('yay' == a); | ||
yield work | ||
assert('wahoo' == b); | ||
})('yay', 'wahoo', done); | ||
}); | ||
}) | ||
}) |
@@ -34,2 +34,16 @@ | ||
}) | ||
it('should ignore non-thunkable properties', function(done){ | ||
co(function *(){ | ||
var res = yield { | ||
name: { first: 'tobi' }, | ||
age: 2, | ||
address: read('index.js', 'utf8') | ||
}; | ||
res.name.should.eql({ first: 'tobi' }); | ||
res.age.should.equal(2); | ||
res.address.should.include('exports'); | ||
})(done); | ||
}) | ||
}) |
@@ -12,7 +12,7 @@ | ||
describe('co.call(receiver)', function(){ | ||
describe('co(receiver).call(ctx)', function(){ | ||
it('should set immediate gen receiver', function(done){ | ||
co.call(ctx, function *(){ | ||
co(function *(){ | ||
assert(ctx == this); | ||
})(done); | ||
}).call(ctx, done); | ||
}) | ||
@@ -30,6 +30,6 @@ | ||
co.call(ctx, function *(){ | ||
co(function *(){ | ||
assert(ctx == this); | ||
yield foo; | ||
})(done); | ||
}).call(ctx, done); | ||
}) | ||
@@ -43,21 +43,8 @@ | ||
co.call(ctx, function *(){ | ||
co(function *(){ | ||
assert(ctx == this); | ||
yield foo; | ||
})(done); | ||
}).call(ctx, done); | ||
}) | ||
it('should pass args', function(done){ | ||
function foo(done) { | ||
assert(this == ctx); | ||
done(); | ||
} | ||
co.call(ctx, function *(a){ | ||
assert('yay' == a); | ||
assert(ctx == this); | ||
yield foo; | ||
})('yay', done); | ||
}) | ||
it('should set join delegate generator receiver', function(done){ | ||
@@ -76,6 +63,6 @@ function *baz() { | ||
co.call(ctx, function *(){ | ||
co(function *(){ | ||
assert(ctx == this); | ||
yield [foo, bar, baz]; | ||
})(done); | ||
}).call(ctx, done); | ||
}) | ||
@@ -99,7 +86,37 @@ | ||
co.call(ctx, function *(){ | ||
co(function *(){ | ||
assert(ctx == this); | ||
yield [foo, bar, baz]; | ||
})(done); | ||
}).call(ctx, done); | ||
}) | ||
}) | ||
describe('co(receiver)(args...)', function(){ | ||
it('should pass arguments to the receiver', function(done){ | ||
co(function *(a, b, c){ | ||
assert(a == 1); | ||
assert(b == 2); | ||
assert(c == 3); | ||
})(1, 2, 3, done); | ||
}) | ||
it('should not pass the callback to the receiver', function(done){ | ||
co(function *(a, b, c){ | ||
assert(arguments.length == 3); | ||
})(1, 2, 3, done); | ||
}) | ||
it('should work when less arguments are passed than expected', function(done){ | ||
co(function *(a, b, c){ | ||
assert(a == 1); | ||
assert(arguments.length == 1); | ||
})(1, done); | ||
}) | ||
it('should work without a callback', function(){ | ||
co(function *(a, b, c){ | ||
assert(a == 1); | ||
assert(arguments.length == 1); | ||
})(1); | ||
}) | ||
}) |
@@ -15,9 +15,2 @@ | ||
describe('co(fn)', function(){ | ||
it('should have the same receiver', function(done){ | ||
var foo = { thread: co }; | ||
foo.thread(function *(){ | ||
this.should.equal(foo); | ||
})(done); | ||
}) | ||
describe('with no yields', function(){ | ||
@@ -79,2 +72,16 @@ it('should work', function(done){ | ||
describe('when an error is passed then thrown', function(){ | ||
it('should only catch the first error only', function(done){ | ||
co(function *() { | ||
yield function (done){ | ||
done(new Error('first')); | ||
throw new Error('second'); | ||
} | ||
})(function(err){ | ||
err.message.should.equal('first'); | ||
done(); | ||
}); | ||
}) | ||
}) | ||
describe('when an error is passed', function(){ | ||
@@ -284,3 +291,51 @@ it('should throw and resume', function(done){ | ||
}) | ||
describe('when no callback is provided', function(){ | ||
it('should rethrow', function(done){ | ||
var addProcessListeners = removeProcessListeners(); | ||
process.once('uncaughtException', function(err){ | ||
err.message.should.equal('boom'); | ||
addProcessListeners(); | ||
done(); | ||
}) | ||
co(function *(){ | ||
yield function (done) { | ||
setImmediate(function () { | ||
done(new Error('boom')); | ||
}) | ||
} | ||
})(); | ||
}) | ||
it('should rethrow on a synchronous thunk', function(done){ | ||
var addProcessListeners = removeProcessListeners(); | ||
process.once('uncaughtException', function(err){ | ||
err.message.should.equal('boom'); | ||
addProcessListeners(); | ||
done(); | ||
}) | ||
co(function *(){ | ||
yield function (done) { | ||
done(new Error('boom')); | ||
} | ||
})(); | ||
}) | ||
}) | ||
}) | ||
}) | ||
function removeProcessListeners(){ | ||
// Remove mocha listeners first. | ||
var listeners = process.listeners('uncaughtException'); | ||
process.removeAllListeners('uncaughtException'); | ||
return function addProcessListeners(){ | ||
listeners.forEach(function(listener){ | ||
process.on('uncaughtException', listener); | ||
}); | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
41343
27
1215
354