UNPKG

queen

Version:

A platform for running scripts on many browsers

170 lines (136 loc) 4.58 kB
var EventEmitter = require('events').EventEmitter, utils = require('./utils.js'), its = require('its'); var create = exports.create = function(workerConfig, options){ options = options || {}; var workforce = new Workforce(workerConfig, options.runUrl); if(options.stopHandler) workforce.stopHandler = options.stopHandler; if(options.workerHandler) workforce.workerHandler = options.workerHandler; if(options.providerFilter) workforce.providerFilter = options.providerFilter; if(options.uniquenessFilter) workforce.uniquenessFilter = options.uniquenessFilter; if(options.killOnStop !== void 0) workforce.killOnStop = options.killOnStop; return workforce; }; var Workforce = exports.Workforce = function(workerConfig, runUrl){ its.object(workerConfig, 'Worker config object must be defined'); var self = this; this.emitter = new EventEmitter(); this.workers = []; this.workerConfig = workerConfig; this.pendingWorkers = 0; this.runUrl = runUrl; // If proxied this.uniquenessMap = {}; // keeps track of uniqueness so workers don't run on the 'same' environments this.kill = utils.once(this.kill.bind(this)); this.api = Object.freeze(getApi(this)); }; var getApi = function(workforce){ var api = workforce.broadcast.bind(workforce); api.on = workforce.emitter.on.bind(workforce.emitter); api.removeListener = workforce.emitter.removeListener.bind(this.emitter); api.kill = workforce.kill; api.start = workforce.start.bind(workforce); api.stop = workforce.stop.bind(workforce); api.populate = workforce.populate.bind(workforce); api.runUrl = workforce.runUrl; return api; }; Workforce.prototype.workerHandler = utils.noop; Workforce.prototype.stopHandler = utils.noop; Workforce.prototype.providerFilter = function(){return true;}; Workforce.prototype.uniquenessFilter = void 0; Workforce.prototype.killOnStop = true; Workforce.prototype.started = false; Workforce.prototype.start = function(){ var self = this, timeout; if(this.started) return; this.started = true; this.emitter.emit('start'); }; Workforce.prototype.populate = function(workerProviders){ if(this.timedout) return; var self = this; if(!utils.isArray(workerProviders)) workerProviders = [workerProviders]; if(workerProviders.length === 0) return; workerProviders = workerProviders.filter(function(provider){ return self.providerFilter(provider.attributes); }); if(self.uniquenessFilter){ workerProviders = workerProviders.filter(function(provider){ var hash = self.uniquenessFilter(provider.attributes); if(hash in self.uniquenessMap) return false; self.uniquenessMap[hash] = true; return true; }); } this.pendingWorkers += workerProviders.length; workerProviders.forEach(function(workerProvider){ workerProvider(self.workerConfig, function(worker){ self.pendingWorkers--; if(worker !== void 0){ self.addWorker(worker, self.workerConfig); } }); }); if(this.pendingWorkers === 0 && self.workers.length === 0){ this.signalStop(); } }; Workforce.prototype.signalStop = function(){ this.stopHandler(); if(this.killOnStop){ this.kill(); } }; Workforce.prototype.stop = function(){ this.workers.concat([]).forEach(function(worker){ worker.kill(); }); }; Workforce.prototype.kill = function(){ this.workers.concat([]).forEach(function(worker){ worker.kill(); }); this.dead = true; this.emitter.emit('dead'); this.emitter.removeAllListeners(); }; Workforce.prototype.broadcast = function(message){ this.workers.forEach(function(worker){ worker(message); }); }; Workforce.prototype.addWorker = function(worker, workerConfig){ its.func(worker, 'Worker expected to be a function'); var self = this, timeout; workerConfig = workerConfig || {}; this.workers.push(worker); if(workerConfig.timeout){ timeout = setTimeout(function(){ worker.kill('timeout'); }, workerConfig.timeout); } worker.on('dead', function(){ if(workerConfig.timeout){ clearTimeout(timeout); } // Remove the provider from the uniqueness map so another may join if // this workforce is continous if(self.uniquenessFilter){ var hash = self.uniquenessFilter(worker.provider.attributes); if(hash in self.uniquenessMap){ delete self.uniquenessMap[hash]; } } self.workers.splice(self.workers.indexOf(worker), 1); if(self.pendingWorkers === 0 && self.workers.length === 0){ self.signalStop(); } }); worker.on('message', function(message){ self.emitter.emit('message', message, worker); }); this.workerHandler(worker); this.emitter.emit('worker', worker); };