This document discusses JavaScript generators and how they can be used to simplify asynchronous code. It begins with a simple generator example and then explores more complex use cases like yielding promises, error handling, and parallel processing. Generators allow long-running operations to be written in a synchronous-looking way and are useful for tasks like I/O. When combined with co-routines, they provide a clean way to write asynchronous code that looks synchronous.
E S 6G E N E R AT O R S
R A M E S H N A I R
H I D D E N TA O . C O M
!
A U G U S T 1 3 , 2 0 1 4
TA I P E I J A VA S C R I P T E N T H U S I A S T S
2.
G E NE R AT O R
A Generator generates values
…
Until no more values are available
3.
S I MP L E G E N E R AT O R
var helloWorld = function*() {!
yield 'hello';!
yield 'world';!
}
This function
returns
a generatorThis generator
“yields”
two strings
4.
S T EP - B Y- S T E P
{ value: 'hello', done: false }
var gen = helloWorld();
var helloWorld = function*() {!
yield 'hello';!
yield 'world';!
}
var value1 = gen.next();
console.log( value1 );
var value2 = gen.next();
console.log( value2 );
{ value: 'world', done: false }
var value3 = gen.next();
console.log( value3 );
{value: undefined, done: true}
5.
F I BO N A C C I
var fib = function*() {
let [prev, curr] = [0, 1];
for (;;) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
1, 2, 3, 5, 8, 13, 21, …
6.
F O R- O F
var fib = function*() {
let [prev, curr] = [0, 1];
for (;;) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
!
for (let n of fib()) {
print(n); // 1, 2, 3, 5, 8, 13,…
}
7.
But yielding valuesisn’t that useful on its own
What if we could feed values back in?
8.
F E ED I N G
{ value: 'hello', done: false }
var gen = helloWorld();
var value1 = gen.next();
console.log( value1 );
var value2 = gen.next(‘my’);
console.log( value2 );
{ value: ‘my world', done: false }
var helloWorld = function*() {!
var v = yield 'hello';!
yield v + ' world';!
}
A yield
is
synchronous
Y I EL D P R O M I S E S
Looks like
a synchronous
call!
var gen = showUser();
var promise = gen.next().value;
promise.then(function(user) {!
! gen.next(user);!
});
var showUser = function*() {!
var user = yield $.get(“/getUser?id=1”);!
alert( user.name );!
}
11.
Y I EL D M A N Y P R O M I S E S
var gen = showStats();
var promise1 = gen.next().value;
promise1.then(function(user) {!
!
!
!
!
});
var showStats = function*() {!
var user = yield $.get(“/getUser?name=bob”);!
var stats = yield $.get(“/stats/” + user.id);!
! …!
}
Repeats
! ! promise2.then(function(stats) {!
! ! ! gen.next(stats);!
! ! });!
var promise2 = gen.next(user).value;
12.
C O -R O U T I N E S
var co = function(gen) {!
! (var _next = function(res) {!
! ! var yielded = gen.next(res);!
! ! if (!yielded.done) {!
! ! ! yielded.value.then(_next);!
! ! }!
! })();!
}!
!
co(showStats());
var showStats = function*() {!
var user = yield $.get(“/getUser?name=bob”);!
var stats = yield $.get(“/stats/” + user.id);!
! …!
}
13.
E R RO R S
var gen = showUser();
var promise = gen.next().value;
promise.then(function(user) {!
! gen.next(user);!
})
var showUser = function*() {!
!
!
!
!
!
}
! .catch(function(err) {!
!! gen.throw(err);!
! });
yield $.get(“/getUser?id=1”);
try {!
! ! !
} catch (err) {!
// do something!
}!
Can yield
inside here
14.
G E NE R AT E D E R R O R S
var gen = showUser();
try {!
! var promise = gen.next().value;!
} catch (err) {!
console.error(err);!
}!
var showUser = function*() {!
throw new Error(‘fail’);!
}
15.
B E TT E R C O - R O U T I N E
var co = function(gen) {
(var _next = function(err, res) {
try {
var yld = null;
!
if (err) {
yld = gen.throw(err);
} else {
yld = gen.next(res);
}
!
if (!yld.done) {
yld.value.then(function(result){
_next(null, result);
}).catch(_next);
}
} catch (err) {
console.error(err);
}
})();
};
16.
C O -R O U T I N E M O D U L E S
• Bluebird
• Promise.coroutine()
• Returns a Promise
• Lets you yield promises. Must configure it to support other item types.
• co
• co()!
• Accepts a callback
• Lets you yield promises, thunks, generators, generator functions
Faster for
Promises
More flexible
yielding
17.
P R OM I S E S - > G E N E R AT O R S
var readFile = // returns a Promise
!
var main = function() {
return readFile('file1')
.then(function(contents) {
console.log(contents);
return readFile('file2');
})
.then(function(contents) {
console.log(contents);
})
}
!
main().catch(…);
var readFile = // returns a Promise
!
var main = function*() {
console.log(yield readFile('file1'));
console.log(yield readFile('file2'));
}
!
co(main)(…);
18.
PA R AL L E L P R O C E S S I N G
var readFile = // returns a Promise
!
var main = function*() {
var files = [
yield readFile('file1'),
yield readFile('file2')
];
... // do stuff with files
}
!
co(main)();
var readFile = // returns a Promise
!
var main = function*() {
var files = yield [
readFile('file1'),
readFile('file2')
];
... // do stuff with files
}
!
co(main)();
sequential parallel
19.
E X PR E S S - > K O A
var express = require('express');
var app = express();
!
app.use(function(req, res, next){
res.send('Hello World');
});
!
app.listen(3000);
var koa = require('koa');
var app = koa();
!
app.use(function*(next){
this.body = 'Hello World';
});
!
app.listen(3000);
Easier to control order of middleware execution…
20.
K O AM I D D L E WA R E
• Waigo (waigojs.com) - web framework built around Koa
var koa = require('koa');
var app = koa();
!
app.use(function*(next) {
try {
yield next;
} catch (err) {
// handle err
}
});
!
app.use(function*(next){
throw new Error('test');
});
Execute rest
of chain
21.
A L SO C H E C K O U T…
• Iterators
• Simpler than generators
• Only return values, no feeding back in
• await!
• ES7 onwards