UNPKG

@thi.ng/csp

Version:

Primitives & operators for Communicating Sequential Processes based on async/await and async iterables

105 lines (104 loc) 2.57 kB
import { shuffle } from "@thi.ng/arrays/shuffle"; import { assert } from "@thi.ng/errors/assert"; import { Channel } from "./channel.js"; const broadcast = async (src, dest, close = true) => { for await (let x of src) { for (const chan of dest) chan.write(x); } if (close) { for (const chan of dest) chan.close(); } }; const concat = async (dest, chans, close = true) => { return (async () => { for (const c of chans) { await consumeWith(c, (x) => dest.write(x)); } close && dest.close(); })(); }; const consume = async (chan, res = [], num = Infinity) => { for (let n = 0; !chan.closed() && n < num; n++) { const x = await chan.read(); if (x == void 0) break; res.push(x); } return res; }; const consumeWith = async (chan, fn, num = Infinity) => { for (let n = 0; !chan.closed() && n < num; n++) { const x = await chan.read(); if (x == void 0) break; fn(x, chan); } }; const drain = async (chan) => await (async () => { const ops = []; for (let i = 0, n = chan.writes.length + chan.queue.length; i < n; i++) { ops.push(chan.read()); } return Promise.all(ops); })(); const fromAsyncIterable = (src, close = true) => pipe(src, new Channel(), close); const merge = (src, dest, close = true) => { assert(src.length > 0, "no inputs given"); dest = dest || new Channel(); (async () => { while (true) { const [x, ch] = await select(...src); if (x === void 0) { src.splice(src.indexOf(ch), 1); if (!src.length) { close && dest.close(); break; } } else { await dest.write(x); } } })(); return dest; }; const into = async (dest, src, close = false) => { for await (let x of src) { if (!dest.writable()) break; await dest.write(x); } close && dest.close(); }; const pipe = (src, dest, close = true) => { into(dest, src, close); return dest; }; const select = async (input, ...rest) => { const inputs = shuffle([input, ...rest]); const sel = await Promise.race(inputs.map((x) => x.race())); for (const chan of inputs) { if (chan !== sel) chan.races.shift(); } if (sel.writes.readable()) { const [msg, write] = sel.writes.read(); write(true); sel.updateQueue(); return [msg, sel]; } return [void 0, sel]; }; const timeout = (delay) => { const ch = new Channel(); setTimeout(() => ch.close(), delay); return ch; }; export { broadcast, concat, consume, consumeWith, drain, fromAsyncIterable, into, merge, pipe, select, timeout };