Javascript Generators
In browser
In nodejs (unstable)
$ nvm install v0.11.13
$ node -v
0.11.13
$ node --harmony generators.js
function* two() {
yield 1;
yield 2;
}
var gen = two();
console.log( gen.next() ); // { value: 1, done: false }
console.log( gen.next() ); // { value: 2, done: false }
console.log( gen.next() ); // { value: undefined, done: true }
console.log( gen.next() ); // Error: Generator has already finished
Iterator
function* odd(limit) {
for (var i = 0; i < limit; i++) {
if (i % 2) yield i;
}
}
for (var i of odd(10)) {
console.log(i);
}
// 1 3 5 7 9
Infinite sequences
function fibonacci(){
var fn1 = 1;
var fn2 = 1;
while (1){
var current = fn2;
fn2 = fn1;
fn1 = fn1 + current;
yield current;
}
}
var sequence = fibonacci();
console.log(sequence.next()); // 1
console.log(sequence.next()); // 1
console.log(sequence.next()); // 2
console.log(sequence.next()); // 3
console.log(sequence.next()); // 5
function* fibonacci(){
var fn1 = 1;
var fn2 = 1;
while (1){
var current = fn2;
fn2 = fn1;
fn1 = fn1 + current;
var reset = yield current;
if (reset){
fn1 = 1;
fn2 = 1;
}
}
}
var sequence = fibonacci();
console.log(sequence.next()); // 1
console.log(sequence.next()); // 1
console.log(sequence.next()); // 2
console.log(sequence.next()); // 3
console.log(sequence.next(true)); // 1
console.log(sequence.next()); // 1
console.log(sequence.next()); // 2
Recursion
function* factorial(n) {
return n === 0 ? 1 : n*(yield* factorial(n-1));
}
var gen = factorial(5);
console.log(gen.next()); // { value: 120, done: true }
fs.readdir(source, function(err, files) {
if (err) {
console.log('Error finding files: ' + err)
} else {
files.forEach(function(filename, fileIndex) {
gm(source + filename).size(function(err, values) {
if (!err) {
widths.forEach(function(width, widthIndex) {
// ...
}.bind(this))
}
})
})
}
}
Read JSON
function readJSONSync(filename) {
return JSON.parse(fs.readFileSync(filename, 'utf8'))
}
Async
function readJSON(filename, callback) {
fs.readFile(filename, 'utf8', function (err, res){
if (err) return callback(err)
callback(null, JSON.parse(res))
})
}
Async 2
function readJSON(filename, callback) {
fs.readFile(filename, 'utf8', function (err, res){
if (err) return callback(err)
try {
callback(null, JSON.parse(res))
} catch(ex) {
callback(ex);
}
})
}
Async 3
function readJSON(filename, callback) {
fs.readFile(filename, 'utf8', function (err, res){
if (err) return callback(err)
try {
res = JSON.parse(res)
} catch (ex) {
return callback(ex)
}
callback(null, res)
})
}
Promise
var Promise = Promise || require('es6-promise').Promise;
function readFile(filename) {
return new Promise(function(resolve, reject) {
fs.readFile(filename, function(err, res) {
err ? reject(err) : resolve(res);
});
});
}
Async with promise
function readJSON(filename) {
return readFile(filename).then(function (res) {
return JSON.parse(res)
});
}
function readJSON(filename){
return readFile(filename).then(JSON.parse);
}
So what's wrong with Promises?
readJSON(file1).then(function(content1) {
// do something
return readJSON(file2);
}).then(function(content2) {
// ...
}).then(function() {
// ...
}).then(function() {
// ...
});
Sequence of operations
db.connect(options).then(function() {
return db.getPostById(postId);
}).then(function(post) {
var tags = post.tags.split(',');
return Q.all(tags.map(function(tag) {
return db.getPostsByTag(tag);
})).then(function(taggedPosts) {
db.disconnect();
});
});
Read JSON sync
function readJSONSync(filename) {
return JSON.parse(fs.readFileSync(filename, 'utf8'))
}
Read JSON async
var readJSON = handle(function* (filename) {
return JSON.parse(yield readFile(filename, 'utf8'));
});
Sequence of operations
handle(function* () {
yield db.connect(options);
var post = yield db.getPostById(postId);
var tags = post.tags.split(',');
var taggedPosts = tags.map(function(tag) {
return yield db.getPostsByTag(tag);
});
db.disconnect();
})();
Error handling
handle(function* () {
try {
var content1 = yield read(file1);
var content2 = yield read(file2);
var content3 = yield read(file3);
} catch(e) {
console.log("Error: " + e.message);
}
})();
How it works?
function* read() {
var content = yield readFile('basic.js');
console.log(content.length);
}
var gen = read();
var promise = gen.next().value;
promise.then(gen.next.bind(gen), gen.throw.bind(gen));
function handle(fn) {
return function() {
var generator = fn.apply(this, arguments);
function next(result) {
return result.done ? result.value : result.value.then(function (res){
return next(generator.next(res))
}, function (err) {
return next(generator.throw(err))
});
}
return next(generator.next());
}
}
var suspend = require('suspend');
suspend(function* (resume) {
return JSON.parse(yield fs.readFile(__filename, 'utf8', resume));
});
function read(file) {
return function(fn){
fs.readFile(file, 'utf8', fn);
}
}
var co = require('co');
co(function *(){
var a = yield read('.gitignore');
var b = yield read('Makefile');
var c = yield read('package.json');
console.log(a.length);
console.log(b.length);
console.log(c.length);
})();
var generator = Q.async(function* () {
var ten = yield 10;
console.log(ten, 10);
var twenty = yield ten + 10;
console.log(twenty, 20);
var thirty = yield twenty + 10;
console.log(thirty, 30);
return thirty + 10;
});
generator().then(function (forty) {
console.log(forty, 40);
}, function (reason) {
console.log("reason", reason);
});
app.post('/users', Q.async(function* (req, res) {
var user = new User(req.params);
if (yield user.save()) {
res.send( JSON.stringify(user) );
} else {
res.send(422);
}
}));
Next generation web framework for node.js
var koa = require('koa');
var app = koa();
// x-response-time
app.use(function *(next){
var start = new Date;
yield next;
var ms = new Date - start;
this.set('X-Response-Time', ms + 'ms');
});
$ mocha --harmony test.js
describe('Some stuff', function() {
// ...
it('should do async operation', Q.async(function* (done) {
var a = yield b();
done();
}));
});
Async
function(args, function(err, res) {
function(args2, function(err, res) {
function(args3, function(err, res) {
// ...
});
});
});
Promises
func().then(function(res) {
// ...
}).then(function(res) {
// ...
}).then(...);
Generators + Promises
async(function* () {
var res = yield func();
// ...
})();
With async await in JavaScript
var fn = handle(function* () {
var content = yield readFile(filename);
// ...
});
async function fn() {
var content = await readFile(filename);
// ...
}
Google Traceur Compiler
https://github.com/google/traceur-compilerFacebook Regenerator
http://facebook.github.io/regenerator/Slides: http://bit.do/nodegenerators