UNPKG

rafa

Version:

Rafa.js is a Javascript framework for building concurrent applications.

108 lines (99 loc) 3.78 kB
// # constructor(capacity: Int, action: String): Channel // A Channel object is a concurrent communication proxy between disparate // readers and writers. Writers are informed of the result of their attempt // to send a message to a reader, which is either that the value written was // send to a reader, buffered and waiting for another reader, or dropped // because there is neither room in the buffer or a reader available. Readers // either receive a value immediately if a value is available in the buffer, // is registered to receive the next value written, or dropped if there // is already a reader registered. // // The channel also implements the Enumerator interface by exposing a next // method. // // A channel can be configured in a number of ways: // // * Buffer capacity can be set by including a `capacity` property in the settings // object; `Channel(1)`. // * Action is a string that is either "drop" or "rotate". It affects how // the channel reacts when the buffer is full. By default, the channel // will drop values when the buffer is full. When action is set to "rotate", // the channel will keep the most recent values and drop old ones. function Channel(capacity = 0, action) { this.action = action || null; this.buffer = capacity ? this.buffer(capacity, action === "rotate") : null; this.callback = null; } inherit(Channel, Rafa, { // # close() // Close the channel. close() { this.buffer = null; this.callback = true; }, // closed(): Bool // Return true if this channel is closed. closed() { return this.buffer === null && this.callback === true; }, // configure(capacity: Int, action: String): Channel // Increase the capacity of this channel's buffer if given a bigger capacity and // change the full action if action is different. configure(capacity = 0, action) { const buffer = this.buffer; const overwrite = action === "rotate"; if (buffer) { if (capacity > buffer.capacity) buffer.configure(capacity, overwrite); } else if (capacity) this.buffer = new this.RingBuffer(capacity, overwrite); return this; }, // # next(callback: A => _) // Iterator implementation. If there are values in the queue, the callback // is called immediately, otherwise the callback is registered. Will throw // an error is a callback is already registered. next(callback) { if (this.callback) throw new Error("Callback already defined"); this.read(value => callback(value && value.isMessage ? value : this.message(value))); }, // # read(callback: A => _): Int // Read a value from this channel by providing a callback that accepts // a single value `A => undefined`. Returns `1` if the callback was called with // a values from the queue, `2` if the callback was registered to receive // the next value when it is written, and `0` if a callback was already // registered. read(callback) { const buffer = this.buffer; let result = 0; if (!this.callback) { if (buffer && buffer.capacity && buffer.length) { callback(buffer.shift()); result = 1; } else { this.callback = callback; result = 2; } } return result; }, // # write(value: A): Int // Write a value to the channel. Returns an integer representing the result, // `1` if the value was forwarded to a reader, `2` if enqueued, `3` if // enqueued after rotating, and `0` if dropped. write(value) { const { callback, buffer } = this; let result = 0; if (callback) { this.callback = null; callback(value); result = 1; } else if (buffer && buffer.capacity) { result = buffer.push(value); if (result) result++; } return result; } });