Spaces:
Runtime error
Runtime error
| const SharedDispatch = require('./shared-dispatch'); | |
| const log = require('../util/log'); | |
| /** | |
| * This class serves as the central broker for message dispatch. It expects to operate on the main thread / Window and | |
| * it must be informed of any Worker threads which will participate in the messaging system. From any context in the | |
| * messaging system, the dispatcher's "call" method can call any method on any "service" provided in any participating | |
| * context. The dispatch system will forward function arguments and return values across worker boundaries as needed. | |
| * @see {WorkerDispatch} | |
| */ | |
| class CentralDispatch extends SharedDispatch { | |
| constructor () { | |
| super(); | |
| /** | |
| * Map of channel name to worker or local service provider. | |
| * If the entry is a Worker, the service is provided by an object on that worker. | |
| * Otherwise, the service is provided locally and methods on the service will be called directly. | |
| * @see {setService} | |
| * @type {object.<Worker|object>} | |
| */ | |
| this.services = {}; | |
| /** | |
| * The constructor we will use to recognize workers. | |
| * @type {Function} | |
| */ | |
| this.workerClass = (typeof Worker === 'undefined' ? null : Worker); | |
| /** | |
| * List of workers attached to this dispatcher. | |
| * @type {Array} | |
| */ | |
| this.workers = []; | |
| } | |
| /** | |
| * Synchronously call a particular method on a particular service provided locally. | |
| * Calling this function on a remote service will fail. | |
| * @param {string} service - the name of the service. | |
| * @param {string} method - the name of the method. | |
| * @param {*} [args] - the arguments to be copied to the method, if any. | |
| * @returns {*} - the return value of the service method. | |
| */ | |
| callSync (service, method, ...args) { | |
| const {provider, isRemote} = this._getServiceProvider(service); | |
| if (provider) { | |
| if (isRemote) { | |
| throw new Error(`Cannot use 'callSync' on remote provider for service ${service}.`); | |
| } | |
| return provider[method].apply(provider, args); | |
| } | |
| throw new Error(`Provider not found for service: ${service}`); | |
| } | |
| /** | |
| * Synchronously set a local object as the global provider of the specified service. | |
| * WARNING: Any method on the provider can be called from any worker within the dispatch system. | |
| * @param {string} service - a globally unique string identifying this service. Examples: 'vm', 'gui', 'extension9'. | |
| * @param {object} provider - a local object which provides this service. | |
| */ | |
| setServiceSync (service, provider) { | |
| if (this.services.hasOwnProperty(service)) { | |
| log.warn(`Central dispatch replacing existing service provider for ${service}`); | |
| } | |
| this.services[service] = provider; | |
| } | |
| /** | |
| * Set a local object as the global provider of the specified service. | |
| * WARNING: Any method on the provider can be called from any worker within the dispatch system. | |
| * @param {string} service - a globally unique string identifying this service. Examples: 'vm', 'gui', 'extension9'. | |
| * @param {object} provider - a local object which provides this service. | |
| * @returns {Promise} - a promise which will resolve once the service is registered. | |
| */ | |
| setService (service, provider) { | |
| /** Return a promise for consistency with {@link WorkerDispatch#setService} */ | |
| try { | |
| this.setServiceSync(service, provider); | |
| return Promise.resolve(); | |
| } catch (e) { | |
| return Promise.reject(e); | |
| } | |
| } | |
| /** | |
| * Add a worker to the message dispatch system. The worker must implement a compatible message dispatch framework. | |
| * The dispatcher will immediately attempt to "handshake" with the worker. | |
| * @param {Worker} worker - the worker to add into the dispatch system. | |
| */ | |
| addWorker (worker) { | |
| if (this.workers.indexOf(worker) === -1) { | |
| this.workers.push(worker); | |
| worker.onmessage = this._onMessage.bind(this, worker); | |
| this._remoteCall(worker, 'dispatch', 'handshake').catch(e => { | |
| log.error(`Could not handshake with worker: ${e}`); | |
| }); | |
| } else { | |
| log.warn('Central dispatch ignoring attempt to add duplicate worker'); | |
| } | |
| } | |
| /** | |
| * Fetch the service provider object for a particular service name. | |
| * @override | |
| * @param {string} service - the name of the service to look up | |
| * @returns {{provider:(object|Worker), isRemote:boolean}} - the means to contact the service, if found | |
| * @protected | |
| */ | |
| _getServiceProvider (service) { | |
| const provider = this.services[service]; | |
| return provider && { | |
| provider, | |
| isRemote: Boolean((this.workerClass && provider instanceof this.workerClass) || provider.isRemote) | |
| }; | |
| } | |
| /** | |
| * Handle a call message sent to the dispatch service itself | |
| * @override | |
| * @param {Worker} worker - the worker which sent the message. | |
| * @param {DispatchCallMessage} message - the message to be handled. | |
| * @returns {Promise|undefined} - a promise for the results of this operation, if appropriate | |
| * @protected | |
| */ | |
| _onDispatchMessage (worker, message) { | |
| let promise; | |
| switch (message.method) { | |
| case 'setService': | |
| promise = this.setService(message.args[0], worker); | |
| break; | |
| default: | |
| log.error(`Central dispatch received message for unknown method: ${message.method}`); | |
| } | |
| return promise; | |
| } | |
| } | |
| module.exports = new CentralDispatch(); | |