/js-caf

JaveScript communicating async functions

Primary LanguageJavaScript

JavaScript Communicating Async Functions (JS-CAF)

JS-CAF (JavaScript communicating asynchronous functions) grew out of a need to model distributed network software designs. A search for available JavaScript concurrency libraries yielded an extensive list: async-csp, Communicating Sequential Processes: an alternative to async generators, We need channels (CSP section), Generators and Channels in JavaScript, f5io/csp, plus others. These all proved to either not provide the desired communicating sequential process channel model or to be more complex than needed.

JS-Caf consists of a single, small (77 lines), dependency-free module exposing a Channel class that, coupled with ES2017 async functions, are all that is needed to build a Commuinicating Sequential Process (CSP) type system.

A Channel instance is a one way channel in either a closed or not closed state over which objects can be sent and received.

A CSP is defined as an async function that sends (await ch.send(msg)) and receives (msg = await ch.get()) messages over a Channel instance. The async function can await on these operations and will resume execution when the promise resolves at some future time.

Here's an example showing JS-CAF use and features:

 1 import {Channel} from './Channel.mjs';
 2  
 3 async function f1(ch, {key}){
 4   await f2Chan.send(`hello`);
 5   await f3Chan.send('hello');
 6   f2Chan.close(); // line 16 is never reached without this
 7   let msg = await ch.get(key);
 8   console.log(`f1 received ${msg}`);
 9 };
10
11 async function f2(ch, {args}){
12   for await (const msg of ch){
13     console.log(`f2(${JSON.stringify(args)}) received ${msg}`);
14     await f1Chan.send(`${msg} back`);
15   }
16   console.log(`f2Chan is closed`);
17 };
18
19 async function f3(ch){
20   console.log(`f3 received ${await ch.get()}`);
21 };
22
23 // Order is important. f2Chan and f3Chan must be defined when f1 starts
24 const f2Chan = new Channel;
25 f2(f2Chan, {args: {arg1: 1}});
26
27 const f3Chan = new Channel;
28 f3(f3Chan);
29

Using

Two methods are available to use js-caf.

Local

Using SSH:

git clone git@github.com:boblund/js-caf.git

Github.io

Channel.mjs can be imported directly from github.io.

import {Channel} from 'https://boblund.github.io/js-caf/Channel.mjs'

The --experimental-network-imports option is required to use this method in nodejs, i.e.

node --experimental-network-imports file.mjs

Class Channel

Channel([options]) <Channel>

Create a new Channel instance in the not closed state.

const chan = new Channel();

chan.close() <undefined>

Close the channel.

chan.close()

chan.closed() <Boolean>

Returns true/false if the channel is closed/not closed.

if(chan.closed()) ...

chan.get() <Promise>|null

Wait for a message. Returns a Promise that resolves to the next message in the channel or null if the channel is closed.

const msg<Promise> = await chan.get();

chan.send(msg) <Promise>

Put a message at the end of the channel and return a Promise. The Promise resolves to true, or false if the channel is closed.

const r<Promise> = await chan.send(msg)

chan.*[Symbol.asyncIterator] <Promise>

Async iterator that waits for a message. Returns a Promise that resolves to the next message or null if the channel is closed.

for await (const msg<Object> of chan<Channel>) { ... }

Channel.anyIdx([promise1, ..., promiseN>]) <Promise>

Static method that returns a Promise that resolves to an object {idx<Number>, msg<Object>} where idx is the index of the first promise in the promise array parameter to resolve and msg is result of that promise. idx == -1 and msg == null if all the promises reject.

const {idx, msg} = await Channel.anyIdx([chan1, ...])

License

Software license: Creative Commons Attribution-NonCommercial 4.0 International

THIS SOFTWARE COMES WITHOUT ANY WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.