jsketcher/modules/bus/index.js
2018-01-17 19:19:33 -08:00

113 lines
2.7 KiB
JavaScript

export default class Bus {
constructor() {
this.listeners = {};
this.state = {};
this.keepStateFor = new Set();
this.lock = new Set();
this.stateConnections = new Map();
}
subscribe(token, callback) {
let listenerList = this.listeners[token];
if (listenerList === undefined) {
listenerList = [];
this.listeners[token] = listenerList;
}
listenerList.push(callback);
return callback;
};
unSubscribe(key, callback) {
const listenerList = this.listeners[key];
for (let i = 0; i < listenerList.length; i++) {
if (listenerList[i] === callback) {
listenerList.splice(i, 1);
return;
}
}
};
tokensToStates(tokens) {
return tokens.map( token => this.state[token] );
}
connectToState(tokens, callback) {
if (!Array.isArray(tokens)) {
tokens = [tokens];
}
let connection = () => {
callback(this.tokensToStates(tokens));
};
tokens.forEach(token => {
if (!this.keepStateFor.has(token)) {
throw `unable to connect to stateless token ${token}. state for a token should be initialized before connecting`;
}
this.subscribe(token, connection);
});
this.stateConnections.set(connection, tokens);
return connection;
}
disconnectFromState(connection) {
this.stateConnections.get(connection).forEach(token => this.unSubscribe(token, connection));
this.stateConnections.delete(connection);
}
updateStates(tokens, updater) {
let updated = updater(this.tokensToStates(tokens));
for (let i = 0; i < tokens.length; ++i) {
this.dispatch(tokens[i], updated[i]);
}
}
updateState(token, updater) {
this.dispatch(token, updater(this.state[token]));
}
setState(token, partialState) {
this.dispatch(token, Object.assign({}, this.state[token], partialState));
}
getState(fqn) {
return this.state[createToken(fqn)];
}
dispatch(key, data) {
if (this.lock.has(key)) {
console.warn('recursive dispatch');
return
}
if (this.keepStateFor.has(key)) {
this.state[key] = data;
}
this.lock.add(key);
try {
let listenerList = this.listeners[key];
if (listenerList !== undefined) {
for (let i = 0; i < listenerList.length; i++) {
const callback = listenerList[i];
try {
callback(data);
} catch(e) {
console.error(e);
}
}
}
} finally {
this.lock.delete(key);
}
};
enableState(forToken, initValue) {
this.keepStateFor.add(forToken);
this.state[forToken] = initValue;
}
}
export function createToken(...fqn) {
return fqn.join('.');
}