read

ES6 Generators

With Christmas come early this coming June, JavaScript is getting a shiny new coat of paint. ES6 comes with a lot of great new features. In particular, if you haven't tried generators yet, or if you have, and they still don't make sense, this post might be helpful.

ES6 (node-harmony, iojs) with generators are already common in Node.js. Checkout Koa.js by the original creators of Express. So why don't we see them much on the client-side? The client-side environment is harder to control, but with transpilation we can avoid this problem.

ES6 Setup

In order to get started with ES6 today, you're going to need to change your JavaScript into standard ES5 code. This is called transpilation. If you plan on supporting older browsers in the future, you might as well get used to the process, as its a great way to use ES6 & proposed ES7 features today.

You can get started with ES6 in the browser today using a real-time compiler such as Traceur or the pre-compiler Babel (formerly named 6to5). Or if generators are the only thing you're looking to add to your code, Facebook has a project called Regenerator.

An easy way to get started is to use the Yeoman generator gulp-angular. It provides options for a setup for either Traceur or 6to5. Unfortunately, I'm still looking for a better quick-start option for handling modules, perhaps with JSPM (ideas?).

Human Readable Async

Generators can help make your async code look human readable again. Let's compare traditional callback methods with generators. Spoiler alert: generators are way better.

Callbacks

function callbackFunction (var1) {  
    first(success, failure) {
        if (success) {
            getSecondDependency(var1, success, failure) {
            if (success {
                second(secondDependency, success, failure) {
                if (success) {
                    third(success, failure) {}
                    fourth(sucess, failure) {}
                 }}}}}}}
  • Plus one for side scrollability.

And the contender: Generators with spawn().

ES6 Generators with Spawn

function generatorWithSpawn (var1) {  
   spawn(function* () {
    try {
      yield first();
      var secondDependency = yield getData(var1);
      yield Promise.all(
        second(secondDependency),
        third(),
        fourth()
      );
   }
   catch (error) {
       console.error(error);
   }
   });
});
  • Plus one for readability.
  • Plus one for error handling.
  • Plus one for easy modification.

How Generators & Spawn Work

spawn() simply wraps around your generator function* and runs the generator until it is complete.

Whenever a process is async, you yield to it. Yield acts like a callback, saying 'pause here until the data comes back.'

Spawn calls the next() iterator when every yield is complete until finished. Use a try & catch blocks for better error handling.

Rather than $q, you can use Promise, another es6 feature to handle calls. There's a better example of using Promises and Spawn here.

Keep in mind that spawn functions are intended to return anything so there's no need to return anything. The spawn() function simply returns undefined.

Setup Spawn in Angular

Adding spawn as a service in Angular is pretty simple. This service is basically just taken from Jake Archibalds blog post here. and tossed into Angular.

/* ES6 Generator - Spawn 
 * Thanks to http://jakearchibald.com/2014/es7-async-functions/ */
(function () {
  'use strict';
  angular.module('angularEs6Spawn', [])
    .factory('spawn', spawn);
function spawn() {  
    return function (generatorFunc) {
      function continuer(verb, arg) {
        var result;
        try {
          result = generator[verb](arg);
        } catch (err) {
          return Promise.reject(err);
        }
        if (result.done) {
          return result.value;
        } else {
          return Promise.resolve(result.value).then(onFulfilled, onRejected);
        }
      }
      var generator = generatorFunc.apply(this, arguments);
      var onFulfilled = continuer.bind(continuer, "next");
      var onRejected = continuer.bind(continuer, "throw");
      return onFulfilled();
    };
  }

Drop that code in and add the dependency 'angularEs6Generator` to your app.

Follow-up

Generators are great. Read more about what you can do:

  • next() can be called with a value passed to the yield statement. For example:
next(function(a) { return a + 1; }  

This is potentially quite valuable. Check out an example here.

  • CSP - check out the ping pong game example.

Read More

It's even easier than it looks. Read more about ES6 Generators:

Blog Logo

Shawn McKay

Published

Image

ShMcK

JavaScript Web Dev

Back Home