For those who are not familiar w/ ES6 generators, here's an illustration:
Consider an asynchronous app that does several IO operations. Using the currently standard javscript callback style, an application might look like this:
app.authenticate(function(loggedIn) { app.fileExists("bla.txt", function(exists) { app.readFile("bla.txt", function(text) { app.sendToClient(text, function() { app.log("operation successful") }) }) }) })As you can see there's a lot of nesting once you start doing several things in sequence. As it turns out, this causes all sort of problems:
- hard to throw and catch errors effectively
- lots of boilerplate code
- even more boilerplate code when doing parallel asynchronous operations
- difficult to reason about non-trivial code (e.g. what is in scope where, when there are many parallel things happening?)
function *myGenerator(a) { yield a; }The` yield` keyword works almost exactly like `return`, except that if you run the function again, the code will continue executing from the next statement after the yield, instead of running the whole function again. (another difference is that yield is an expression) For example:
function *countTo3() { console.log(1); yield; console.log(2); yield; console.log(3); } countTo3(); // logs 1 countTo3(); // logs 2 countTo3(); // logs 3With this framework, we can then rewrite that nested application kinda like this:
function *myApp() { var loggedIn = yield app.authenticate() var exists = yield app.fileExists("bla.txt") var text = yield app.readFile("bla.txt") yield app.sendToClient(text) app.log("operation successful") }So basically, every time you call `yield` you're explicitly telling the app to "go away and do other stuff" while waiting for expensive IO operations, which makes it very easy to reason about the code, and comes with all the goodness of readability, proper exception stacking, etc.
Very interesting stuff.