Why Can’t Anyone Write a Simple es6 Generators Tutorial

Unimaginative….I know, just write another blog post that chains off of the title of your previous. Well be it that I code my brains out during the week (my boss tells me I shouldn’t bring my laptop home and should go to bed earlier so I’m not so grumpy at the office…but it never happens) it’s hard to muster the energy to use my off time to write a blog post about code. But, being that I’m a glutton for punishment and there is a plethora of cocky nerds out there (potentially myself included), giving overcomplicated explanations of useful content and tooling, I thought I’d give this another shot with my most recent obsession…es6 Generators.

Yeh, yeh, I’m late to the party and opinionated members of the community like @killercup will probably just tell me to create a Make file https://news.ycombinator.com/item?id=9954973 and my only commenter @Sebastian Burgstaller on https://medium.com/@dtothefp/why-can-t-anyone-write-a-simple-webpack-tutorial-d0b075db35ed#.hfovbe83c is probably going to get mad because I’m going to use profanity in un-constructive ways, but fuck it, when you think about code all week/weekend you might as well make writing about it fun.

Ok, from the top let’s get things straight. In my opinion the main thing that Generators make cool about your code is in relation to async operations.

  • They make your async code look synchronous. Data in callbacks can be extracted into variables and data inside the `then` of a “thenable” (think promises here) can magically be extracted into variables outside of the promise’s callback scope without having to do something like `let something;` and then changing the value of `something` later.
  • Error handling sucks much less. You can use straight up `try/catch` and no more errors getting swallowed inside of Promises.

Yeh, you can be a neckbeard and think it’s so cool to make a generators example of how you can create something that could potentially be a dangerous process/browser crashing infinite loop like a fibonacci sequence or a `while` loop that never ends but is that really benefiting any of us in

  1. learning generators,
  2. 2) using them in real life, or
  3. 3) not getting royally pissed off and deciding we’ll learn generators another day or just scrap JavaScript all together and write e-books Kyle Rush.

Here’s a great example: https://davidwalsh.name/es6-generators

WTF => are you kidding me???

Ok, maybe I’m just an idiot and every other hipster in the world had a look at this and it was immediately apparent what was going on, but I’d be willing to bet if you received a Pull Request for the above code and had never seen generators before you might ask for a little better explanation of what was going on. So below will serve as the “idiots” guide to the above example, and that said I’m the first idiot to sign up.

Maybe that is a little more helpful, although you are still most likely having the “when the fuck will I ever use this in real life” moment. Don’t worry, that’s expected at this point. Sit back, crack a Narragansett tall boy, and trust that there’s a use case in here somewhere.

Now let’s get to the fun stuff. Before that let me share a couple links that got me started:

BTW if you know this Chris Buttery dude give him a shout out for his awesome website and CSS transitions, I hope he did it all using Generators.

Anyway, both of the above links were extremely eye opening but still left me with the “fuck my life, I’m the biggest idiot in the world” feeling until I made my own little example https://gist.github.com/dtothefp/a0a6a5c2e3dae8bc0d0f with about a million code comments and banged my head against the wall for about a week long period.

Ok, again let’s get another thing straight, Generators aren’t actually very useful without a lot of extra setup. This setup generally comes in the form of a “thunk” function and some sort of “run” (generally used for functions that return their data via a callback) or “spawn” (generally used for Promises returning data in `then` or `catch`, this won’t need a “thunk”). The magic comes from the fact that when a generator is instantiated and `.next()` is called, data can be magically extracted out of the generator function, and in this case it’s not some sort of un-useful fuck’n `x + “some bullshit”` example, it will be a “thunkified” function or a Promise, (or actually whatever you `yield` it if your a big enough hipster to build libraries such as CO and VO. There is actually a pretty great blog post explaining CO line by line http://webuniverse.io/asynchronous-programming-with-ES6-generators-promises-and-npm-co/ although it looks like this guy took web design advice from Chris Buttery (god I wish I had a “neckbeard” emoji on Medium).

Thunks and The `run` Function

Don’t let “thunks” scare you. They are basically just a fancy name for “curry”ing and if you don’t know what “curry” means, don’t worry about it. You can make your own “thunk” or “curry” functions or use a library such as Lodash, Rambda, or probably one of the thousands of “thunk” modules on NPM. Here’s an article giving examples of both in relation to generators http://blog.vullum.io/nodejs-javascript-flow-fibers-generators/, but for now lets just show a small example that I use in my build code

Now let’s not freak out too quickly and accuse my Gist of being borderline cocky nerd status….it’s unfortunate how hard it is to address generators without going part way there. Read the comments, customize your .vimrc, and then read on to the `run` function because that is what makes all this seemingly useless setup “useful” in the end.

This is really getting complicated to explain and I’m starting to feel bad for complaining about neckbeards when mine is growing at a rapid pace while writing this tutorial. Let’s break this shit down into terms that someone who learned JavaScript from an animated clay object could understand:

  • our `run` function get’s called with a generator as it’s only argument
  • inside this generator it does a bunch of async shit, which is cool because you can catch errors and write code without it successively migrating off of the right side of your IDE
  • internally in the `run` function it instantiates the generator straight away, giving us the ability to call `.next()`
const it = genFn();
  • we then call our private `_next` function with no arguments to start the process, and internally to the private function it extracts the “thunked” function from `yield` off of the `value` property from the instantiated generator object.
const cont = it.next(val);
if (cont.done) return;
const cb = cont.value; //gets the "thunked" function
  • the extracted function is then called with the private function `_next` as it’s only argument.
cb(_next); //cb is the "thunked" function, `_next` get's called    
//sometime in the future
  • when the async action is complete our “thunked” function we retrieved from `yield` calls the private `_next` function with the typical Node arguments of error (first arg) and data (second arg)
  • this time around when `_next` reaches the statement `const cont = it.next(val);` it “injects” the returned value from the `cb` into the `yield`, or if it encountered an error it will use the `throw` method on the generator to `throw` the error.
  • this process will continue:
    a) retrieving a “thunked” function and calling it with `_next` as it’s argument
    b) starting the generator back up when the `cb` (`_next`) is called internally by the “thunked” function and throwing and error or injecting a value into `yield`
  • the process completes when the last `next` method is called, we execute all of the code after our final `yield`, and receive an object back with the signature
res = {done: true, value: 'undefined'}
  • notice in our example, the final value is `undefined` because nothing is returned from the generator. If we chose to return something, it would be defined in the final `value.

Ok, I take it all back…I am a cocky nerd, neckbeard, and claymation porcupine wrapped all into one. If you’ve made it this far into the post please start questioning your sanity and mine, and/or get a life that involves a lot more fresh air and a lot less “thunking”.

Promises and `spawn`

Wait up….all you cocky nerds and esNext hipsters don’t start yelling all at once that the following content is irrelevant because the proposed `async` functions will encompass generator functionality when `yield`ing promises (in the case of async functions we don’t `yield` but in fact `await`). It’s true, async functions are in the proposal for sometime in the future and you can use them on Babel already (I’m guessing using “stage-0” and “babel-polyfill” but don’t quote me on that. I’m sure there is some sort of Babel 6 plugin for this that you have to specify with 1,000 other plugins/presets to get it to work because Babel 6 is soooooo hipster that it actually doesn’t do anything unless you tell it too. Oh sorry, it does do something, it breaks all of your previous code for 3 months until there is actually enough community documentation to figure it out.…sorry ranting :-P). Anyway, I think it’s important in our generator journey to understand how to handle Promises, and different from async functions we could mix together a function, if we wanted to, that would handle Promises, callbacks, whatevs all together in one happy place…but I’m not going to go there..just use CO or VO in that case.

Regardless, let’s have a look at the typical `spawn` function that I think most hipsters who write generators articles yoinked directly from the Q promise library.

And, let’s break it down:

  • since we’re becoming generator pros at this point we recognize that the generator is instantiated straight away in the `spawn` function, and the internal private function, in this case `_co` is called immediately.
  • this time the private function takes a method name in the form of a string, and the initial method to call is `next` on the generator instance
res = it[method](arg);
  • so the first time around the generator has `next` called on it and the Promise is returned from the `yield`.
  • we now continue on to resolving the Promise. We do this with `Promise.resolve` because as the `_co` function is called recursively we may be resolving a Promise or we may just be resolving a non-Promise value. `Promise.resolve` is handy for this as it can resolve seemingly anything in a “thenable” way. On top of this, the `spawn` function itself returns a Promise which is similar to how the up and coming `async` function behaves.
  • once the Promise is resolved and we retrieve a value or an error, we call `_co` again, with the appropriate method name as it’s first argument, and the value as it’s second.
  • and now that it’s all starting to click….if the `method` is not “throw” we will inject the value back in through the generators `yield` statement making our async code look all happy and synchronous.

Now…..I know my cocky nerd status meter is in danger of going off the charts, but let’s calm it down a little by breaking down a multiple step example. Also, feel free to play along in the Babel repl https://babeljs.io/repl/ and `console.log` to your heart’s content.

“Simple” Ex.

The above “simple” example resolves your promise and magically extracts it’s value. You could `console.log` `someVal`, and you can also resolve data that is returned from the `spawn` function and stored in `fromSpawn`…that is => if you `return` from your generator. But, in our example above we return nothing from our generator, therefore when we call `then` on the Promise returned from our `spawn` function, it resolves but with no data and you would see the output below if you wanted to test in the Babel repl.

But, if we decide to return something from the generator

we will see a little different output

I think if I try to explain this my mind might explode, so throw it in the Babel repl, log it out, and report back with nerd bashing comments on this post.

Error Handling

Now….sigh…..to error handling.

First of all let’s not let our outer and inner cocky nerds get the best of us, and we’ll add a ton of logging no matter how big of a “noob” it makes us look like. Also, if you want to be an even bigger hipster/noob you can use some great debugging tools like node-inspector or iron-node but these can be a little tricky to run with es6 compilation, that is if you don’t want to pre-compile, and they worthy of being a topic of their own blog post.

I’ll be referring to the above logging in the following cases so don’t be ashamed to have a look back 1 or 1000 times. I know I’m doing the same as I’m cringing while I write this article.

Case 1— “throwing” inside of spawn

So, let’s start with a “simple” example where we accidentally do something that “throws” inside of our generator that is passed as the only argument to `spawn`. Lot’s of comments about the logging might be helpful here so don’t be bashful even if your inner cocky nerd starts degrading you:

  • TRY — first invocation of `_co` with the `next` method, we retrieve our Promise from `yield`
  • RET SPAWN PROMISE — `_co` returns the original call of `Promise.resolve`
  • THEN RESOLVE — 1000ms later our promise resolves the first argument (the callback function) of `Promise.resolve` is called with the resolved data. `_co` is then called recursively with the `next` method and the resolved data.
  • TRY — passes the resolved data back into `yield` to be stored in the `someVal` constant.
  • CATCH — this is trippy and definitely the part I’m most unclear on. Whatever happens between this last `yield` and the completion of the generator function seems to be executing in the `try` block so when we encounter our `undefined` variable the exception is caught and we return `Promise.reject` (update kinda understand this now, this all happens in the last invocation of `.next` so if you have an error in there it will be caught). This in turn in returned up the chain to where `_co` was called recursively so we `return` the `Promise.reject` inside of `Promise.resolve` which in turn was returned from the original invocation of `_co`….ugghhhhhh, sorry.
  • Err from spawn — notice we didn’t `return` anything from the generator function, but our Promise that we received from the `spawn` function invokes the callback passed to it’s `catch` method with the error caught inside of the recursively called `_co` function.

Silence in the room…I think I will just continue and pretend like that was all extremely clear.

Case 2 — yielding a “rejected” Promise

Since we are dealing with promises we could potentially run into that arguably horrible error swallowing behavior that they are so notorious for. But, since we are in magical generator land we are hoping that we will be saved from such a fate.

And checkout the logging

Let’s go through line by line:

  • TRY — inside the first `try` retrieves the Promise from `yield`
  • RET SPAWN PROMISE — the `_co` function returns the `Promise.resolve` from returned in the last `else` statement
  • THEN ERR — `_co` gets called recursively inside of that promise’s error handler (the second argument to `Promise.resolve`) => this is async and happens 1000ms later when the Promise rejects
  • TRY-after generator restarts instead of “injecting” a value into `yield` we call the “throw” method on the generator instance.
  • Inside spawn error — this is the `catch` block inside of the generator function, and we passed the error message by calling the generator instance “throw” method with it as an argument
  • End of spawn — this is the log at the end of the function where we choose not to `return` anything. Notice the variable `someVal` is `undefined`
  • DONE THROW — again…this is trippy, well at least to me, when we called “throw” on the generator instance, we began executing code in the generator function until it’s end, then the code after the last attempted resolution of yield in `_co` was executed until we reached the check to see if the generator instance was `done` (ie res.done). At this point because the last method was “throw” we return the `arg` from the generator (ie what was originally rejected in our initial `yield`ed Promise).
  • Data from spawn — if you made it this far, this logging comes from the Promise returned by `spawn`. Weird right because didn’t `return` anything from our generator. This is because if the last method passed to `_co` is “throw” then we `return` `arg` rather than `res.value`. And now that we are all generator pro’s we know that if we don’t return anything from a generator `res.value` when `res.done = true` will be `undefined`.

Woooo, that was a little bit horrible and I’m vowing to not write another blog post again (until I think something else is cool…which is pretty much everyday), and fully expecting to get death threats from cocky nerds and noobs alike. Therefore, I might as well leave you with the thought that I didn’t even cover for…of loops in relation to `yield`ing inside of a loop in a generator function, and probably lots of other examples. That said, you can as always have a glimpse into my Chrome bookmarks and find useful tips on this and many other aspects I’ve omitted and that other hipsters in the community would be happy to rant about.