Spaces:
Build error
Build 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(); | |