reactive-channel
Version:
A simple yet powerful abstraction that enables communication between asynchronous tasks.
128 lines (127 loc) • 3.31 kB
JavaScript
import { NotEnoughAvailableSlotsQueueError as e, NotEnoughFilledSlotsQueueError as t, makeCircularQueue as n } from "reactive-circular-queue";
import { makeDerivedStore as r, makeStore as i } from "universal-stores";
//#region src/lib/index.ts
var a = () => void 0, o = class extends Error {
constructor() {
super("channel full, cannot enqueue data");
}
}, s = class extends Error {
constructor() {
super("channel closed");
}
}, c = class extends Error {
constructor() {
super("channel has already too many pending recv");
}
};
function l(e) {
let { capacity: t = 1024, maxConcurrentPendingRecv: l = 1024 } = e || {}, u = n(t), d = n(t), f = n(l), p = i(!1), m = r([p, u.availableSlots$], ([e, t]) => e ? 0 : t), h = r([p, u.filledSlots$], ([e, t]) => e ? 0 : t), g = r(m, (e) => e > 0), _ = r([h, f.full$], ([e, t]) => e > 0 && !t);
async function v(e, t) {
if (p.content()) throw new s();
if (u.full$.content()) throw new o();
let n = a, r = a, i = new Promise((e, t) => {
n = e, r = t;
}), c;
f.empty$.content() ? (c = {
promise: i,
resolveSend: n,
rejectSend: r
}, u.enqueue(c), d.enqueue(e)) : f.dequeue().resolveRecv({
promise: i,
resolveSend: n,
rejectSend: r,
value: e
});
try {
if (!t?.signal) await i;
else {
let e = t.signal;
e.throwIfAborted(), await Promise.race([i, new Promise((t, n) => {
e.addEventListener("abort", () => {
Promise.resolve().then(() => n(e.reason)).catch(a);
});
})]);
}
} catch (e) {
if (c) {
let e = u.indexOf(c);
e !== -1 && (u.remove(e), d.remove(e));
}
throw e;
}
}
function y(e) {
if (p.content()) throw new s();
if (u.full$.content()) throw new o();
v(e).catch(a);
}
async function b(e) {
if (p.content()) throw new s();
let t;
if (!u.empty$.content()) t = {
...u.dequeue(),
value: d.dequeue()
};
else {
if (f.full$.content()) throw new c();
let n = {
resolveRecv: a,
rejectRecv: a
}, r = new Promise((e, t) => {
n.resolveRecv = e, n.rejectRecv = t, f.enqueue(n);
});
try {
if (!e?.signal) t = await r;
else {
let n = e.signal;
n.throwIfAborted(), t = await Promise.race([r, new Promise((e, t) => {
n.addEventListener("abort", () => {
Promise.resolve().then(() => t(n.reason)).catch(a);
});
})]);
}
} catch (e) {
let t = f.indexOf(n);
throw t !== -1 && f.remove(t), e;
}
}
return t.resolveSend(), t.value;
}
async function* x() {
for (; u.filledSlots$.content() > 0;) yield await b();
}
function S() {
if (p.content()) return;
p.set(!0);
let e = new s();
for (let t of f) t.rejectRecv(e);
for (let t of u) t.rejectSend(e);
d.clear();
}
return {
buffer: d,
tx: {
send: y,
sendWait: v,
canWrite$: g,
closed$: p,
close: S,
availableOutboxSlots$: m,
capacity: t
},
rx: {
pendingRecvPromises$: f.filledSlots$,
recv: b,
iter: x,
[Symbol.asyncIterator]: x,
canRead$: _,
closed$: p,
close: S,
capacity: t,
filledInboxSlots$: h
}
};
}
//#endregion
export { s as ChannelClosedError, o as ChannelFullError, c as ChannelTooManyPendingRecvError, e as NotEnoughAvailableSlotsQueueError, t as NotEnoughFilledSlotsQueueError, l as makeChannel };
//# sourceMappingURL=index.js.map