Mikk Kirstein / @kolmas
ES 6 generators*
Generators are functions which can be exited and later re-entered.
Their context (variable bindings) will be saved across re-entrances.
functions that can be paused ...
function* myGen() {
console.log('margus');
var a = yield 2;
console.log('mahler');
var b = yield 5;
return a + b;
}
function* myGen() {
console.log('margus');
var a = yield 2;
console.log('mahler');
var b = yield 5;
return a + b;
}
var it = myGen();
log(it);
// myGen {}
Iterator object
it.next(/* val */); // { value: ?, done: true|false };
it.throw(err); // { value: ?, done: true|false };
function* myGen() {
console.log('margus');
var a = yield 2;
console.log('mahler');
var b = yield 5;
return a + b;
}
var it = myGen();
log(it);
// myGen {}
log(it.next());
// margus
// Object {value: 2, done: false}
The yield keyword is used to pause and resume a generator function.
A communication point between the iterator and the generator
function* myGen() {
console.log('margus');
var a = yield 2;
console.log('mahler');
var b = yield 5;
return a + b;
}
var it = myGen();
log(it);
// myGen {}
log(it.next());
// margus
// Object {value: 2, done: false}
log(it.next());
// mahler
// Object {value: 5, done: false}
function* myGen() {
console.log('margus');
var a = yield 2;
console.log('mahler');
var b = yield 5;
return a + b;
}
var it = myGen();
log(it);
// myGen {}
log(it.next());
// margus
// Object {value: 2, done: false}
log(it.next());
// mahler
// Object {value: 5, done: false}
log(it.next());
// ?
function* myGen() {
console.log('margus');
var a = yield 2;
console.log('mahler');
var b = yield 5;
return a + b;
}
var it = myGen();
log(it);
// myGen {}
log(it.next());
// margus
// Object {value: 2, done: false}
log(it.next());
// mahler
// Object {value: 5, done: false}
log(it.next());
// Object {value: NaN, done: false}
function* myGen() {
console.log('margus');
var a = yield 2;
console.log('mahler');
var b = yield 5;
return a + b;
}
var it = myGen();
log(it);
// myGen {}
log(it.next());
// margus
// Object {value: 2, done: false}
log(it.next());
// mahler
// Object {value: 5, done: false}
log(it.next());
// Object {value: NaN, done: false}
log(it.next());
// Object {value: undefined, done: true}
function* fib() {
var a = 0;
var b = 1;
var c = 0;
while (true) {
c = a;
a = b;
b += c;
yield a;
}
}
function* fib() {
var a = 0;
var b = 1;
var c = 0;
while (true) {
c = a;
a = b;
b += c;
yield a;
}
}
var it = fib();
function* fib() {
var a = 0;
var b = 1;
var c = 0;
while (true) {
c = a;
a = b;
b += c;
yield a;
}
}
var it = fib();
log(it.next().value); // 1
function* fib() {
var a = 0;
var b = 1;
var c = 0;
while (true) {
c = a;
a = b;
b += c;
yield a;
}
}
var it = fib();
log(it.next().value); // 1
log(it.next().value); // 1
function* fib() {
var a = 0;
var b = 1;
var c = 0;
while (true) {
c = a;
a = b;
b += c;
yield a;
}
}
var it = fib();
log(it.next().value); // 1
log(it.next().value); // 1
log(it.next().value); // 2
function* fib() {
var a = 0;
var b = 1;
var c = 0;
while (true) {
c = a;
a = b;
b += c;
yield a;
}
}
var it = fib();
log(it.next().value); // 1
log(it.next().value); // 1
log(it.next().value); // 2
log(it.next().value); // 3
function* pow() {
return Math.pow(yield 5, yield 2);
}
function* pow() {
return Math.pow(yield 5, yield 2);
}
var it = pow();
function* pow() {
return Math.pow(yield 5, yield 2);
}
var it = pow();
log(it.next().value);
// 5
function* pow() {
return Math.pow(yield 5, yield 2);
}
var it = pow();
log(it.next().value);
// 5
log(it.next(2).value);
// 2
function* pow() {
return Math.pow(yield 5, yield 2);
}
var it = pow();
log(it.next().value);
// 5
log(it.next(2).value);
// 2
log(it.next(3).value);
function* pow() {
return Math.pow(yield 5, yield 2);
}
var it = pow();
log(it.next().value);
// 5
log(it.next(2).value);
// 2
log(it.next(3).value);
// 8
function* pow() {
return Math.pow(yield 5, yield 2);
}
var it = pow();
log(it.next().value);
// 5
log(it.next(2).value);
// 2
log(it.next(3).value);
// 8
log(it.next());
// { done: true, value: undefined }
Iterating a generator
function* count() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 8;
}
for (var v of count()) {
console.log(v);
}
// 1
// 2
// 3
// 4
// 5
Control Flow!
... back to basics
JavaScript is a language which is
- Dynamic
- Single Threaded
- Asynchronous
- Concurrency based on event loop
- Functions as first class citizens
Callbacks
Callbacks suck because...
they are hard to follow
var usr;
$.get("user", function(user) {
usr = user;
});
console.log(usr);
Callbacks suck because...
its hard to handle errors
// This. Wont. Work.
try {
$.get("user", function(user) {
$.get("posts/" + user.id, function(posts) {
throw new Error('Oops?!');
// Magic...
});
});
} catch (ignored) {
}
$.get("user", function(err, user) {
$.get("posts/" + user.id, function(err, posts) {
// Magic...
});
});
function onError(err) {
// TODO: Dammit, Margus, handle it!
}
$.get("user", function(err, user) {
if (err) {
return onError(err);
}
$.get("posts/" + user.id, function(err, posts) {
if (err) {
return onError(err);
}
// Magic...
});
});
Callbacks suck because...
they are HARD to follow
var data = {};
function login(data) {
$.post('login', data, function(user) {
console.log(user);
});
}
$.get('token', function(token) {
data.token = token;
if (data.token && data.username) {
login(data);
}
});
$.get('username', function(username) {
data.username = username;
if (data.token && data.username) {
login(data);
}
});
var data = {};
function login(data) {
$.post('login', data, function(user) {
console.log(user);
});
}
$.get('token', function(token) {
data.token = token;
if (data.token && data.username) {
login(data);
}
});
$.get('username', function(username) {
data.username = username;
if (data.token && data.username) {
login(data);
}
});
async(function * (resume) {
var data = {}
data.token = yield $.get('token', resume);
data.username = yield $.get('username', resume);
var user = yield $.post('login', data, resume);
console.log(user);
});
async(function * (resume) {
try {
var data = {}
data.token = yield $.get('token', resume);
data.username = yield $.get('username', resume);
var user = yield $.post('login', data, resume);
console.log(user);
} catch (e) {
// OMFG THIS WORKS!?!
console.log(e);
}
});
Putting it all together
as a coroutine
function async(gen) {
var it;
function resume(err, ret) {
if (err) {
return it.throw(err);
}
it.next(ret);
}
it = gen(resume);
it.next();
}
async(function * (resume) {
try {
var data = {}
data.token = yield $.get('token', resume);
data.username = yield $.get('username', resume);
var user = yield $.post('login', data, resume);
console.log(user);
} catch (e) {
// OMFG THIS WORKS!?!
console.log(e);
}
});
Libraries
Available on
- node 0.11+ with harmony
- chrome canary
- firefox nightly
- firefox developer edition
- traceur
ES 6 generators*
By Mikk Kirštein
ES 6 generators*
- 1,236