rafa
Version:
Rafa.js is a Javascript framework for building concurrent applications.
108 lines (99 loc) • 3.78 kB
JavaScript
// # 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;
}
});