import LocalStorageEvent from "./localStorageEvent"; class LocalStorageEmitter { namespace = 'wfc-' /** * * @type {Map} */ subscriptions; /** * * @type {Map} */ handles; /** * * @type {Map} */ invokes; constructor() { window.__lse = this; window.addEventListener('storage', ev => { let key = ev.key; if (!key) { return; } if (!key.startsWith(this.namespace + '$') || !key.endsWith('$')) { return; } let value; try { value = JSON.parse(ev.newValue); } catch (e) { value = null; } if (value === null) { console.log('localStorageEmitter ignore null', key) return; } if (key.endsWith('$r$')) { this.invokes.forEach((invoke, k) => { let invokeReturnValueKey = k + 'r' + '$'; if (invokeReturnValueKey === key) { invoke.callback(value.r); let b = this.invokes.delete(k) localStorage.removeItem(invokeReturnValueKey) localStorage.removeItem(k) } }) } else { this.subscriptions.forEach((subscription, k) => { if (k === key) { subscription.listener(new LocalStorageEvent(this), value.args); if (subscription.once) { this.subscriptions.delete(k); } localStorage.removeItem(k) } }); this.handles.forEach((handle, k) => { if (key.indexOf('$$') === -1 || key.indexOf('$r$') > -1) { return; } let index = key.indexOf('$$'); let invokeKey = key.substr(0, index + 1) if (k === invokeKey) { let invokeId = value.invokeId; let returnValue = handle.listener(new LocalStorageEvent(this), value.args); let invokeReturnValueKey = k + '$' + invokeId + '$' + 'r' + '$'; if (returnValue instanceof Promise) { returnValue.then(v => { let r = {r: v} localStorage.setItem(invokeReturnValueKey, JSON.stringify(r)); }).catch(reason => { console.log('handle failed', reason) }); if (handle.once) { this.handles.delete(k) } } else { let r = {r: returnValue} console.log('handle res', invokeId, returnValue) localStorage.setItem(invokeReturnValueKey, JSON.stringify(r)); if (handle.once) { this.handles.delete(k) } } localStorage.removeItem(invokeKey) } }); } }) this.subscriptions = new Map(); this.handles = new Map(); this.invokes = new Map(); } /** * 订阅通知 * @param {string} channel * @param {function(LocalStorageEvent, args)} listener */ on(channel, listener) { this.subscriptions.set(this.namespace + '$' + channel + '$', {once: false, listener: listener}); } /** * 一次性订阅 * @param {string} channel * @param {function(LocalStorageEvent, args)} listener */ once(channel, listener) { this.subscriptions.set(this.namespace + '$' + channel + '$', {once: true, listener: listener}); } /** * 发布通知 * @param {string} channel * @param {any} args */ send(channel, args) { let tmp = {args: args} localStorage.setItem(this.namespace + '$' + channel + '$', JSON.stringify(tmp)) } // // sendSync(channel, args) { // this.send(channel, args); // } /** * 处理远程调用 * @param {string} channel * @param {function (any)} listener */ handle(channel, listener) { this.handles.set(this.namespace + '$' + channel + '$', {once: false, listener: listener}); } /** * 一次性处理远程调用 * @param {string} channel * @param {function (any)} listener */ handleOnce(channel, listener) { this.handles.set(this.namespace + '$' + channel + '$', {once: true, listener: listener}); } /** * 执行远程调用,调用结果通过callback回调 * @param {string} channel * @param {any} args * @param {function (any)} callback */ invoke(channel, args, callback) { let invokeId = new Date().getTime() + '-' + Math.ceil(Math.random() * 10000); this.invokes.set(this.namespace + '$' + channel + '$$' + invokeId + '$', { invokeId: invokeId, callback: callback }); let tmp = { invokeId: invokeId, args: args } localStorage.setItem(this.namespace + '$' + channel + '$$' + invokeId + '$', JSON.stringify(tmp)) } /** * 执行远程调用,调用结果通过promise返回 * @param channel * @param args * @return {Promise} */ promiseInvoke(channel, args) { return new Promise(((resolve, reject) => { this.invoke(channel, args, v => { resolve(v); }); })) } _dump() { console.log('dump localStorageEmitter') this.subscriptions.forEach((value, key) => { console.log(key, value) }) this.handles.forEach((value, key) => { console.log(key, value) }) this.invokes.forEach((value, key) => { console.log(key, value) }) } } let self = new LocalStorageEmitter(); export default self;