In a JavaScript function, code is asynchronous.
In a JavaScript generator controlled by "co"
now, let's start from the beginning
// in a scope
function* gen() { /* ... */ }
// in an object
const a = {
gen: function* () { /* ... */ }
}
// in a class
class A {
*gen() { /* ... */ }
}function* gen1() {
yield 1;
yield 2;
yield 3;
}
function* gen2() {
yield 0;
yield* gen1(); // delegation
yield 4;
}
// `Array.from` handles iterators, it iterates till the end
const iter = gen2();
console.log(Array.from(iter)); // [0, 1, 2, 3, 4]
const a = [1, 2, 3]; // [1, 2, 3]function* gen() {
yield 1;
yield 2;
yield 3;
}
const a = Array.from(gen()); // [1, 2, 3]Declarative
Programmatic
The control layer gets yielded values wrapped into an object of type:
{value: any, done: boolean}
"yield A" -> {value: A, done: false}
"return B" -> {value: B, done: true}
// iteration code
function* gen() {
yield 1;
yield 2;
yield 3;
}// control code
const iter = gen();
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: undefined, done: true}| GENERATOR | DIR. | CONTROL |
|---|---|---|
| yield message | → | {value: message, done: false} = iter.next(...) |
// resume it once, it's already done
console.log(iter.next(B)); // -> {value: B, done: true}// generate the iterator
var iter = gen();// start it
console.log(iter.next()); // -> {value: A, done: false}function* gen() {
return yield A;
}One generator, different ways to control the iterations.
// iteration code
function* genArgs(...instantiationArgs) {
while (instantiationArgs.length) { console.log(yield instantiationArgs.shift()); }
}One generator, different ways to control the iterations.
// iteration code
function* genArgs(...instantiationArgs) {
while (instantiationArgs.length) { console.log(yield instantiationArgs.shift()); }
}// instantiate iterator
var iter = genArgs(1, 2, 3);// control: resume with undefined, repeated 4 times
console.log(iter.next()); // RESUME WITH UNDEFINED// {value: 1, done: false}
// undefined// {value: 3, done: false}
// undefined// {value: 2, done: false}
// undefined// {done: true}One generator, different ways to control the iterations.
// iteration code
function* genArgs(...instantiationArgs) {
while (instantiationArgs.length) { console.log(yield instantiationArgs.shift()); }
}// instantiate iterator
var iter = genArgs(1, 2, 3);// retain last emit
var emitted = {value: undefined, done: false};// control: resume with previous emitted value, repeated 4 times
console.log(emitted = iter.next(emitted.value)); // RESUME WITH THE EMITTED VALUE// {value: 1, done: false}
// 1
// {value: 2, done: false}
// 2
// {value: 3, done: false}
// 3
// {done: true}One generator, different ways to control the iterations.
// iteration code
function* genArgs(...instantiationArgs) {
while (instantiationArgs.length) { console.log(yield instantiationArgs.shift()); }
}// control code
var iter = genArgs(Promise.resolve(1), Promise.resolve(2), Promise.resolve(3));var chain = Promise.resolve();// promise chain control, repeated
chain = chain.then((value) => {
const emitted = iter.next(value); // RESUME WITH THE RESOLVE VALUE
console.log(emitted);
return emitted.value;
});// {value: Promise<1>, done: false}
// 1
// {value: Promise<2>, done: false}
// 2
// {value: Promise<3>, done: false}
// 3
// {done: true}| GENERATOR | DIR. | CONTROL |
|---|---|---|
| yield message | → | {value: message, done: false} = iter.next(...) |
| message = yield | ← | iter.next(message) |
| GENERATOR | DIR. | CONTROL |
|---|---|---|
| try { yield; } catch(error) {} | → | iter.throw(error) |
| throw error | ← | try { iter.next(...); } catch(error) {} |
To date 2016-09-25
"Generator based control flow goodness for nodejs and the browser, using promises, letting you write non-blocking code in a nice-ish way"
// iteration code
function* genArgs(...instantiationArgs) {
while (instantiationArgs.length) { console.log(yield instantiationArgs.pop()); }
}
// control code
var iter = genArgs(Promise.resolve(1), Promise.resolve(2), Promise.resolve(3));
var chain = Promise.resolve();
// promise chain control, repeated
chain = chain.then((value) => iter.next(value).value);"co" implements this control layer (not exactly)
You will only implement this part
It handles async yielded objects and resolves them before resuming with the resolve value, i.e.
It actually handles the following types:
const customProcess = () => {
const authPromise = authenticate();
authPromise.then(storeTokens);
return authPromise
.then(() => getProfile('me'))
.then(({accountId}) => Promise.all([
getRestaurant(accountId),
getReservations(accountId)
]))
.then(([restaurant, reservations]) => ({
restaurant,
reservations
}));
}const customProcess = co.wrap(function* () {
const auth = yield authenticate();
storeTokens(auth);
const {accountId} = yield getProfile('me');
return yield {
restaurant: getRestaurant(accountId),
reservations: getReservations(accountId),
};
});Promises only
Wrapped with "co"
You write synchronous code, yielding only when it's asynchronous.
The control layer will resume your code when ready ! (and do some more clever things)
Consider it's the same but:
Ensure you're using a recent node version
// execute on file change
while inotifywait -q <filename>; do node $_; doneTo execute your code automatically, you can use:
from package "inotify-tools"
for mac users, you can user "fswatch"
Start from https://github.com/atondelier/coding-dojo-co
"use strict";
const co = require('co');
const wait = (ms) => {
/* implement wait body */
};
co(function* (){
console.log('before');
yield wait(1000);
console.log('after');
});"use strict";
const co = require('co').default;
const _ = require('lodash');
// do request which resolves between 1 and 2 seconds after
const request = (path) => new Promise((resolve) => {
console.log('start ' + path);
setTimeout(
() => {
console.log('finish ' + path);
resolve({path, content: 'content for path ' + path })
},
(1 + Math.random()) * 1000
);
});
co(function* (){
const paths = _.range(0, 10).map((n) => 'path/to/file' + n);
const contents = [];
/* implement sized batching here */
return contents;
}).then(console.log, console.error);
"use strict";
const co = require('co').default;
const _ = require('lodash');
// do request which resolves between 1 and 2 seconds after
const request = (path) => new Promise((resolve) => {
console.log('start ' + path);
setTimeout(() => {
console.log('finish ' + path);
resolve({path, content: 'content for path ' + path })
}, (1 + Math.random()) * 1000);
});
co(function* (){
const paths = _.range(0, 10).map((n) => 'path/to/file' + n);
const contents = [];
/* implement sized parallel process here */
return contents;
}).then(console.log, console.error);