One of the coolest new features coming in ES6 Harmony is support for iterators and generators (learn more). If you aren’t familiar with the concept, generators create iterable sequences of elements that aren’t evaluated until they are iterated through. Generator functions, through the use the yield operator, are similar to coroutines in that they can pause execution and yield control to another part of the program.

A very simple example of a generator in action is:

var countToThree = function* () { yield 1; yield 2; yield 3; }; var iterator = countToThree(); iterator.next(); // 1 iterator.next(); // 2 iterator.next(); // 3

Another important feature of generators is that a caller can also send values back into the to-be-resumed generator function. With this in mind, it was soon realized that when used in conjunction with Promises (representations of “asynchronous values,” standardized in the Promises/A spec), generators could be used to mitigate the “callback hell” that comes as a result of JavaScript’s single threaded nature. The result is vastly cleaner and more straight forward code.

Generators will allow for code like:

// imagine that 'do' is a function that automatically resumes execution of a generator // once a promised value has been resolved. do(function* () { var userId = yield $.get(findUserByNameUrl, { name: "John Doe" }); var birthDate = yield $.get(getBirthDateByUserUrl, { userId: userId }); alert(birthDate); });

Over today’s:

// We could also use the "promises" that jQuery returns in "today's" version, but // either way, it's not as clean as the generator + promises version. $.get(findUserByNameUrl, { name: "John Doe", function (userId) { $.get(getBirthDateByUserUrl, { userId: userId }, function (birthDate) { alert(birthDate); }); });

This is great! I really can’t wait until more browsers start implementing this feature (Node.JS users can get this now by enabling ES6 mode).

But alas, this has already been covered much in depth on several other blogs. There are also many libraries out there that support this. Task.js has been around since October 2010, faithfully waiting until the day that generators are consistently supported in the major browsers. Q.js, which is one of the most fully featured implementations of the Promises/A spec also supports this through its Q.async and Q.spawn utility methods.

At any rate, I think this is a pretty cool concept. How far can we push it?

Combining Web Workers with Generators + Promises

Late last summer, I wrote a small utility/proof of concept called SimpleWorker (@GitHub)that allows for easy, inline definitions of Web Workers. Additionally, it uses Promises to represent the asynchronous results of the computations from the web worker threads.

That said, it should now be apparent that this should allow us to easily delegate computationally expensive work out to web workers.

First, let’s define a naive recursive implementation of the Fibonacci series using SimpleWorker. If this were run in the main thread, it would most definitely lock up the UI as it is CPU bound.

var fibonacci = SimpleWorker(function (n) { var fib = function (n) { if (n === 0 || n == 1) { return n; } return fib(n-1) + fib(n-2); }; return fib(n); });

Now, let’s use it with Q.spawn:

$('form').submit(function () { var $form = $(this), $input = $("#input"), $output = $("#output"); Q.spawn(function () { var n = parseInt($input.val(), 10), result; $form.addClass('processing'); result = yield fibonacci(n); $output.val(result); $form.removeClass('processing'); }) return false; });

You can try it out for yourself on this jsfiddle (Firefox only). If you enter a sufficiently large number (n=45+ on my laptop) your CPU will start churning away at the result – but notice that the UI is still fully responsive and animations continue to run smoothly while it’s doing so.

This is a pretty simple example, but it just goes to show once again how absolutely versatile Promises can be in JavaScript. And when paired along with generators, they are only that much easier to use.

At the time of this post, Firefox is the only browser that has support for generators. Chrome Canary also has support for generators, but the setting must be enabled in about:flags explicitly.