hbus
Version:
An event bus lib.
182 lines (176 loc) • 5.75 kB
JavaScript
class Action {
constructor(type, payload) {
this.type = type;
this.payload = payload;
}
}
function createActionFactory(type, defaultPayload) {
const defaultPayloadIsObject = defaultPayload instanceof Object;
return (payload) => new Action(type, defaultPayloadIsObject && payload instanceof Object ?
Object.assign({}, defaultPayload, payload) :
payload);
}
function createProcessor(processorMap, defaultProcessor) {
return (state, action) => {
const { type } = action;
return (type in processorMap) ? processorMap[type](state, action) :
defaultProcessor ? defaultProcessor(state, action) : state;
};
}
const defaultComparer = (oldState, newState) => {
if (oldState === newState && String(oldState) === String(newState) ||
oldState !== oldState && newState !== newState) {
return true;
}
else {
if (!(oldState instanceof Object && newState instanceof Object)) {
return false;
}
for (const oldKey in oldState) {
if (!(oldKey in newState && defaultComparer(oldState[oldKey], newState[oldKey]))) {
return false;
}
}
for (const newKey in newState) {
if (!(newKey in oldState)) {
return false;
}
}
return true;
}
};
const defaultTickMethod = (callback) => {
requestAnimationFrame(callback);
};
const ticker = {
tickMethod: defaultTickMethod,
_willTick: false,
_callbacks: new Array(),
tick(callback) {
ticker._callbacks.push(callback);
if (!ticker._willTick) {
ticker._willTick = true;
ticker.tickMethod(() => {
ticker._willTick = false;
const { _callbacks } = ticker;
_callbacks.forEach(cb => {
cb();
});
_callbacks.length = 0;
});
}
}
};
class Bus {
constructor(processor, defaultState = {}) {
this.processor = processor;
this.comparer = defaultComparer;
this._stateRequestCallbacks = new Array();
this._willUpdate = false;
this._actions = new Array();
this._subscriberMap = new Map();
this._subscribers = new Array();
this._state = defaultState;
}
_requestUdpate() {
if (!this._willUpdate) {
this._willUpdate = true;
ticker.tick(() => {
this._willUpdate = false;
this._update();
const { _stateRequestCallbacks, _state } = this;
_stateRequestCallbacks.forEach(callback => {
callback(_state);
});
_stateRequestCallbacks.length = 0;
});
}
}
_update() {
const { processor, comparer, _state, _actions, _subscriberMap } = this;
// @ts-ignore
let newState = _state instanceof Object ? Object.create(_state) : _state, t;
_actions.forEach(action => {
t = processor(newState, action);
if (t !== undefined) {
newState = t;
}
});
_actions.length = 0;
this._state = newState;
let hasChanged = false;
_subscriberMap.forEach((subscribers, propName) => {
const prop = newState[propName];
if (!comparer(prop, _state[propName])) {
hasChanged = true;
subscribers.forEach(subscriber => {
subscriber(prop);
});
}
});
if (hasChanged || !comparer(_state, newState)) {
this._subscribers.forEach(subscriber => {
subscriber(newState);
});
}
}
getState() {
return this._state;
}
requestState(callback) {
this._stateRequestCallbacks.push(callback);
this._requestUdpate();
return this;
}
publish(action) {
this._actions.push(action);
this._requestUdpate();
return this;
}
subscribe(subscriber) {
this._subscribers.push(subscriber);
return this;
}
unsubscribe(subscriber) {
const { _subscribers } = this, index = _subscribers.indexOf(subscriber);
if (index >= 0) {
_subscribers.splice(index, 1);
}
return this;
}
clearSubscribers() {
this._subscribers.length = 0;
return this;
}
subscribeProp(propName, subscriber) {
const { _subscriberMap } = this;
let subscribers = _subscriberMap.get(propName);
if (!subscribers) {
_subscriberMap.set(propName, subscribers = []);
}
subscribers.push(subscriber);
return this;
}
unsubscribeProp(propName, subscriber) {
const { _subscriberMap } = this, subscribers = _subscriberMap.get(propName);
if (subscribers) {
const index = subscribers.indexOf(subscriber);
if (index >= 0) {
subscribers.splice(index, 1);
}
}
return this;
}
clearPropSubscribers(propName) {
this._subscriberMap.delete(propName);
return this;
}
clearAllPropSubscribers() {
this._subscriberMap.clear();
return this;
}
clearAllSubscribers() {
return this.clearSubscribers().clearAllPropSubscribers();
}
}
export { Action, createActionFactory, createProcessor, defaultComparer, defaultTickMethod, ticker, Bus };