ES6 Generators Deliver Go Style Concurrency

24 August 2013

Last night I expressed some frustration about the state and future of
concurrency in JavaScript. I ended up having a little bit of back and
forth with David Herman and he
pointed out that ES6 Generators can express Go
and core.async's flavor of
CSP. Now
I had thought about this in the past but I could not see how. Part
of this was that I'd never seen how a CSP system works under the hood
(now I have months of core.async development under my belt)
and part of it was my distraction over the limitations of combining
Generators with Promises.

I'd never considered combining Generators with something else.

What follows is a minimal amount of code that works in Node.js 0.11
with the ES6 harmony command line setting. I'll explain
each part. The insight is to combine Generators with Channels.

This is our low level state machine stepper. machine is the
generator and step is the result of calling that generator at least
once via next. The result of calling next on a Generator is an
object with two fields value, and done.

If the generator is not done we go into a loop. The value should be
a function which attempts to do some work and returns an array
representing an instruction for the machine. The first value in this
array is what the machine should do next. If this value is "park"
then we need to queue ourselves for later execution so we can retry
the function in step.value.

If instruction is "continue" we call next on the machine with the value
portion of the instruction. The yielded process can now continue with the
result of computation to the next step.

This is the actual go function users will call, it kicks things off.

function go(machine) {
var gen = machine();
go_(gen, gen.next());
}

What are channels? Channels are simply queues and the simplest way to
represent them is an array. Here is our first channel operation
that asynchronously puts a value onto a channel. Notice that it returns
the required instruction needed by go_. If the channel is empty we
can place a value in it, if not we park.

It's easy to imagine the sophisticated buffering strategies supported
by Go and core.async by using something other than arrays for channels.