UNPKG

@thi.ng/transducers-async

Version:

Async versions of various highly composable transducers, reducers and iterators

84 lines (83 loc) 2.19 kB
import { MSub } from "./mult.js"; const pubsub = (src, topicFn) => new PubSub(src, topicFn); class PubSub { constructor(src, topicFn) { this.src = src; this.topicFn = topicFn; } topics = /* @__PURE__ */ new Map(); isActive = false; /** * Creates a new subscription (aka custom `AsyncIterable`) which will * receive any future values from `src` matching given topic `id`. The * returned subscription can be removed again via * {@link PubSub.unsubscribeTopic}. */ subscribeTopic(id) { const sub = new MSub(); let subs = this.topics.get(id); if (!subs) { this.topics.set(id, subs = []); } subs.push(sub); if (!this.isActive) { this.isActive = true; this.process(); } return sub; } /** * Similar to {@link PubSub.subscribeTopic}, but for one-off event handling. * Creates a new subscription for topic `id` and waits for next value. Once * received, it immediately unsubscribes again and then calls `fn` with * value. * * @param id * @param fn */ subscribeOnce(id, fn) { const sub = this.subscribeTopic(id); const $this = this; (async () => { for await (let x of sub) { $this.unsubscribeTopic(id, sub); fn(x); } })(); } /** * Attempts to remove given child subscription (presumably created via * {@link PubSub.subscribeTopic}). Returns true if removal was successful. * * @param sub */ unsubscribeTopic(id, sub) { const subs = this.topics.get(id); if (!subs) return false; const idx = subs.findIndex((x) => x === sub); if (idx >= 0) { subs.splice(idx, 1); sub.resolve(void 0); return true; } return false; } async process() { for await (let val of this.src) { const topic = this.topicFn(val); const subs = this.topics.get(topic); if (!subs?.length) continue; for (let s of subs) s.resolve(val); await Promise.all(subs.map((x) => x.notifyP)); } for (let subs of this.topics.values()) { for (let s of subs) s.resolve(void 0); } this.topics.clear(); this.isActive = false; } } export { PubSub, pubsub };