UNPKG

channel-ts

Version:

Channels implemented in Typescript using async/await

135 lines (134 loc) 4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MultiReceiverChannel = exports.SimpleChannel = exports.TryReceivedKind = exports.State = void 0; var State; (function (State) { State["empty"] = "empty"; State["receiver"] = "receiver"; State["data"] = "data"; State["close"] = "close"; })(State = exports.State || (exports.State = {})); var TryReceivedKind; (function (TryReceivedKind) { TryReceivedKind["value"] = "value"; TryReceivedKind["notReceived"] = "no"; TryReceivedKind["close"] = "close"; })(TryReceivedKind = exports.TryReceivedKind || (exports.TryReceivedKind = {})); // Multi Producer Single Consumer Channel class SimpleChannel { constructor() { this.receivers = []; this.data = []; this._state = State.empty; } get state() { return this._state; } tryReceive() { if (this._state == State.data) { const data = this.data.shift(); if (data === null) { this._state = State.close; return { kind: TryReceivedKind.close }; } if (this.data.length === 0) { this._state = State.empty; } return { kind: TryReceivedKind.value, value: data }; } else { return { kind: TryReceivedKind.notReceived }; } } receive() { if (this._state === State.close) { return Promise.reject(); } else if (this._state === State.data) { const data = this.data.shift(); if (data === null) { this._state = State.close; return Promise.reject(); } if (this.data.length === 0) { this._state = State.empty; } return Promise.resolve(data); } else { return new Promise((resolve, reject) => { // executor runs before creating the Promise this.receivers.push([resolve, reject]); this._state = State.receiver; }); } } send(data) { if (this._state === State.close) { throw "sending on closed channel"; } else if (this._state !== State.receiver) { // when no receiver this.data.push(data); this._state = State.data; } else { // at least one receiver is available const [resolve] = this.receivers.shift(); resolve(data); if (this.receivers.length === 0) { this._state = State.empty; } } } close() { if (this._state === State.close) { // when already closed return; } else if (this._state !== State.receiver) { // when no receiver this.data.push(null); this._state = State.data; } else { // at least one receiver is available const [, reject] = this.receivers.shift(); reject(); if (this.receivers.length === 0) { this._state = State.close; } } } async *[Symbol.asyncIterator]() { try { while (true) { yield await this.receive(); } } catch { // assume we're done with the channel } } } exports.SimpleChannel = SimpleChannel; // Multi Producer Multi Consumer Channel class MultiReceiverChannel { constructor() { this.chan = new Set(); } send(data) { for (const chan of this.chan) { chan.send(data); } } close() { for (const chan of this.chan) { chan.close(); } } receiver() { const chan = new SimpleChannel(); this.chan.add(chan); return chan; } removeReceiver(chan) { this.chan.delete(chan); } } exports.MultiReceiverChannel = MultiReceiverChannel;