- Available in ES6 Harmony
- Present in Node
0.11 unstable
and above - Enable via the
--harmony
or--harmony-generators
flag in0.11
Generators are constructor functions for iterators.
Define an iterator constructor function (i.e. a generator) with a *
after the function
keyword.
The iterator instance returned from a generator function has a method next
.
The next
method can be called multiple times on a generator, and each time it will return the result of each consecutuve iteration.
Use the yield
keyword inside the generator to emit this result.
Every time next
is called the function is invoked and it returns an object with value
and done
fields. The value
field contains whatever was yielded, and the boolean done
indicates whether the function has returned/completed yet or not.
Importantly unlike normal non-blocking JS, execution inside the generator function is blocked immediately after a yield, and only resumes when next
is called again.
Example: iterator.js
Example: 'Callback hell' https://github.com/jedrichards/portfolio/blob/master/api/api/routes/auth-routes.js
next
returns its yielded value synchronously, there's nothing intrinsically async about generators.
But where things get interesting is when the yielded value is something other than a primitive value. For example some sort of object that represents an async operation like a promise or a thunk.
When used in this way, usually together with a generator flow control library like co
, we can describe sets of sequential async operations that look like sync code and thus avoid messy nested callbacks and fiddly error handling
Example: thunk.js
The above example works because co
can recognise a thunk being yielded and knows how to handle it. The full list of yielded objects that co
can work with:
- Thunks
- Promises
- More generators (nesting)
- Arrays & objects (for parallel execution)
Example: parallel.js
Flow control using generators turns out to be a nice pattern for web app middleware.
We can use the yield
keyword to avoid nested callbacks in middleware (for example when interacting with other async resources), and create an 'onion skin' like structure. Each middleware yields to the next until a middleware function returns at which point the response is sent and the middleware stack unwinds allowing each middleware to perform any final "post response" actions. Middleware state is retained pre and post response by lieu of the simple fact we're still in the same function scope.
Example: koa-1.js
KoaJS uses co under the hood so we can take advantage of the flow control it provides to construct nested sets of sequential or parallel middleware tasks using thunks, promises and generator syntax.
Error handling is also much improved. No more messy testing null
and err
function arguments like traditional callbacks. Generator and yield syntax means that the call stack is not disrupted like with async code (remember that inside generators syncronous code is just blocked, not left behind) this means that try catch
blocks work as expected.
Example: koa-2.js