@thi.ng/csp
Version:
Primitives & operators for Communicating Sequential Processes based on async/await and async iterables
104 lines (103 loc) • 2.51 kB
JavaScript
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 (let chan of dest) chan.write(x);
}
if (close) {
for (let chan of dest) chan.close();
}
};
const concat = async (dest, chans, close = true) => {
return (async () => {
for (let 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 = [input, ...rest];
const sel = await Promise.race(inputs.map((x) => x.race()));
for (let 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
};