@thi.ng/transducers-async
Version:
Async versions of various highly composable transducers, reducers and iterators
80 lines (79 loc) • 1.93 kB
JavaScript
import { illegalState } from "@thi.ng/errors/illegal-state";
const mult = (src) => new Mult(src);
class Mult {
constructor(src) {
this.src = src;
}
subs = [];
isActive = false;
/**
* Creates a new subscription (aka custom `AsyncIterable`) which will
* receive any future values from `src`. The returned subscription can be
* removed again via {@link Mult.unsubscribe}.
*/
subscribe() {
const sub = new MSub();
this.subs.push(sub);
if (!this.isActive) {
this.isActive = true;
(async () => {
for await (let val of this.src) {
for (let s of this.subs) s.resolve(val);
if (val === void 0) this.subs.length = 0;
if (!this.subs.length) break;
await Promise.all(this.subs.map((x) => x.notifyP));
}
for (let s of this.subs) s.resolve(void 0);
this.subs.length = 0;
this.isActive = false;
})();
}
return sub;
}
/**
* Attempts to remove given child subscription (presumably created via
* {@link Mult.subscribe}). Returns true if removal was successful.
*
* @param sub
*/
unsubscribe(sub) {
const idx = this.subs.findIndex((x) => x === sub);
if (idx >= 0) {
this.subs.splice(idx, 1);
sub.resolve(void 0);
return true;
}
return false;
}
}
class MSub {
valueP;
notifyP;
resolve;
notify;
active = false;
constructor() {
this.$await();
}
async *[Symbol.asyncIterator]() {
if (this.active) illegalState("multiple consumers unsupported");
this.active = true;
while (true) {
const res = await this.valueP;
if (res === void 0) break;
yield res;
this.notify();
this.$await();
}
this.active = false;
}
$await() {
this.notifyP = new Promise((res) => this.notify = res);
this.valueP = new Promise((res) => this.resolve = res);
}
}
export {
MSub,
Mult,
mult
};