channel-ts
Version:
Channels implemented in Typescript using async/await
135 lines (134 loc) • 4 kB
JavaScript
"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;