async and await are, I think, the best thing to happen to Javascript, though I’d very much like something like sweet.js so I can invent it when I needed it most. However, I mostly see them being described in terms of promises. Here, I show how you can implement CSP style channels using them without using promises.

A basic channel

A basic channel can be thought of as a port that supports a post(value)  method, with the value being delivered to whichever end points are consuming values from the channel. What we do is to model this “whichever end points” as the await  points within async  functions.

The code below should be fairly self explanatory if you understand that the semantics of an  async function  are as though it returned a spawned generator.

Here is a simple demo usage of channel which takes N values from the given channel, prints them out, then prints ‘done’.

A sample (sanitized) transcript of a session follows –

 

Posting errors to a channel

With the previous simple approach, we’re guaranteed that both chan.post(val)  and await chan  won’t throw an error. If we want a producer to be able to error out consumers so that consumer code can stay on the happy path within a try-catch  block, we have to modify the channel definition a bit.

 

The same simple demo, but now with a try-catch –

Here is an error flow –

 

Back pressure

The above channel implementations aren’t complete, as they have unbounded buffer accumulation and no back pressure support. They just serve to illustrate a usage of async / await  that I don’t see commonly talked about.

We can model back pressure by making chan.post(val)  itself be usable with an  await , with the result of the await  being the value posted to the channel. With this approach, we have a few choices for error propagation. Below, we choose to propagate an error raised by a producer to all producers and consumers except the producer injecting the error.

 

With the above implementation of a channel with back pressure, you need to have the discipline to call chan.push(val)  only with an await  – like await chan.push(val). If you don’t, then nothing will get pushed down the channel. This is because when consumers are waiting on the channel, there is nothing to pump, but if you call chan.push(val) without await ing, then the values supplied won’t get pumped to the consumers. Vice versa, if the producers act first, there will be no consumers to pump to, but until there are consumers, the producers won’t get to continue.

Here is another silly example –

The above will produce something like –

The order of the ping()  and pong()  calls doesn’t matter.

Other uses of this technique

The core of the technique is that we only need to return an object with a then(onSuccess, onFailure)  method. A real Promise  is not required. Below is a simple example of a fixed delay .

 

Have fun!

Disclaimer: The above is a concept sketch and is not extensively tested.