Spaces:
Build error
Build error
File size: 5,755 Bytes
30c32c8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
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();
|