workerpool
Advanced tools
Comparing version 2.0.0 to 2.1.0
@@ -169,2 +169,3 @@ /******/ (function(modules) { // webpackBootstrap | ||
worker.register = function (methods) { | ||
if (methods) { | ||
@@ -177,2 +178,5 @@ for (var name in methods) { | ||
} | ||
worker.send('ready'); | ||
}; | ||
@@ -179,0 +183,0 @@ |
@@ -7,4 +7,4 @@ /** | ||
* | ||
* @version 2.0.0 | ||
* @date 2016-09-18 | ||
* @version 2.1.0 | ||
* @date 2016-10-11 | ||
* | ||
@@ -83,3 +83,3 @@ * @license | ||
var isBrowser = (typeof window !== 'undefined'); | ||
var environment = __webpack_require__(1); | ||
@@ -92,3 +92,3 @@ /** | ||
exports.pool = function pool(script, options) { | ||
var Pool = __webpack_require__(1); | ||
var Pool = __webpack_require__(3); | ||
@@ -103,17 +103,3 @@ return new Pool(script, options); | ||
exports.worker = function worker(methods) { | ||
var environment = __webpack_require__(4); | ||
if (environment == 'browser') { | ||
// worker is already loaded by requiring worker | ||
// use embedded worker.js | ||
var blob = new Blob([__webpack_require__(6)], {type: 'text/javascript'}); | ||
var url = window.URL.createObjectURL(blob); | ||
importScripts(url); | ||
} | ||
else { | ||
// node | ||
// TODO: do not include worker in browserified library | ||
var worker = __webpack_require__(8); | ||
} | ||
var worker = __webpack_require__(8); | ||
worker.add(methods); | ||
@@ -126,4 +112,7 @@ }; | ||
*/ | ||
exports.Promise = __webpack_require__(2); | ||
exports.Promise = __webpack_require__(4); | ||
exports.platform = environment.platform; | ||
exports.isMainThread = environment.isMainThread; | ||
exports.cpus = environment.cpus; | ||
@@ -134,8 +123,58 @@ /***/ }, | ||
var Promise = __webpack_require__(2), | ||
WorkerHandler = __webpack_require__(3); | ||
// used to prevent webpack from resolving requires on node libs | ||
var node = {require: __webpack_require__(5)}; | ||
var node = {require: __webpack_require__(2)}; | ||
// determines the JavaScript platform: browser or node | ||
module.exports.platform = typeof Window !== 'undefined' || typeof WorkerGlobalScope !== 'undefined' ? 'browser' : 'node'; | ||
// determines whether the code is running in main thread or not | ||
module.exports.isMainThread = module.exports.platform === 'browser' ? typeof Window !== 'undefined' : !process.connected; | ||
// determines the number of cpus available | ||
module.exports.cpus = module.exports.platform === 'browser' | ||
? self.navigator.hardwareConcurrency | ||
: node.require('os').cpus().length; // call node.require to prevent `os` to be required when loading with AMD | ||
/***/ }, | ||
/* 2 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
var map = { | ||
"./Pool": 3, | ||
"./Pool.js": 3, | ||
"./Promise": 4, | ||
"./Promise.js": 4, | ||
"./WorkerHandler": 5, | ||
"./WorkerHandler.js": 5, | ||
"./environment": 1, | ||
"./environment.js": 1, | ||
"./generated/embeddedWorker": 6, | ||
"./generated/embeddedWorker.js": 6, | ||
"./header": 7, | ||
"./header.js": 7, | ||
"./worker": 8, | ||
"./worker.js": 8 | ||
}; | ||
function webpackContext(req) { | ||
return __webpack_require__(webpackContextResolve(req)); | ||
}; | ||
function webpackContextResolve(req) { | ||
return map[req] || (function() { throw new Error("Cannot find module '" + req + "'.") }()); | ||
}; | ||
webpackContext.keys = function webpackContextKeys() { | ||
return Object.keys(map); | ||
}; | ||
webpackContext.resolve = webpackContextResolve; | ||
module.exports = webpackContext; | ||
webpackContext.id = 2; | ||
/***/ }, | ||
/* 3 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
var Promise = __webpack_require__(4); | ||
var WorkerHandler = __webpack_require__(5); | ||
var environment = __webpack_require__(1); | ||
/** | ||
@@ -156,2 +195,5 @@ * A pool to manage workers | ||
this.workers = []; // queue with all workers | ||
this.tasks = []; // queue with tasks awaiting execution | ||
// configuration | ||
@@ -165,13 +207,17 @@ if (options && 'maxWorkers' in options) { | ||
else { | ||
var environment = __webpack_require__(4); | ||
this.maxWorkers = Math.max((environment.cpus || 4) - 1, 1); | ||
} | ||
var numCPUs = (environment == 'browser') | ||
? (window.navigator.hardwareConcurrency || 4) | ||
: node.require('os').cpus().length; // call node.require to prevent `os` to be required when loading with AMD | ||
this.maxWorkers = Math.max(numCPUs - 1, 1); | ||
if (options && 'minWorkers' in options) { | ||
if(options.minWorkers==='max') { | ||
this.minWorkers = Math.max((environment.cpus || 4) - 1, 1); | ||
} else { | ||
if (!isNumber(options.minWorkers) || !isInteger(options.minWorkers) || options.minWorkers < 0) { | ||
throw new TypeError('Option minWorkers must be a positive integer number'); | ||
} | ||
this.minWorkers = options.minWorkers; | ||
this.maxWorkers = Math.max(this.minWorkers, this.maxWorkers); // in case minWorkers is higher than maxWorkers | ||
} | ||
this._ensureMinWorkers(); | ||
} | ||
this.workers = []; // queue with all workers | ||
this.tasks = []; // queue with tasks awaiting execution | ||
} | ||
@@ -301,13 +347,14 @@ | ||
worker.exec(task.method, task.params, task.resolver) | ||
.then(function () { | ||
me._next(); // trigger next task in the queue | ||
}) | ||
.catch(function () { | ||
// if the worker crashed and terminated, remove it from the pool | ||
if (worker.terminated) { | ||
me._removeWorker(worker); | ||
} | ||
me._next(); // trigger next task in the queue | ||
}); | ||
.then(function () { | ||
me._next(); // trigger next task in the queue | ||
}) | ||
.catch(function () { | ||
// if the worker crashed and terminated, remove it from the pool | ||
if (worker.terminated) { | ||
me._removeWorker(worker); | ||
// If minWorkers set, spin up new workers to replace the crashed ones | ||
me._ensureMinWorkers(); | ||
} | ||
me._next(); // trigger next task in the queue | ||
}); | ||
} | ||
@@ -358,3 +405,5 @@ } | ||
var index = this.workers.indexOf(worker); | ||
if (index != -1) this.workers.splice(index, 1); | ||
if (index != -1) { | ||
this.workers.splice(index, 1); | ||
} | ||
}; | ||
@@ -381,2 +430,14 @@ | ||
/** | ||
* Ensures that a minimum of minWorkers is up and running | ||
* @private | ||
*/ | ||
Pool.prototype._ensureMinWorkers = function() { | ||
if (this.minWorkers) { | ||
for(var i = this.workers.length; i < this.minWorkers; i++) { | ||
this.workers.push(new WorkerHandler(this.script)); | ||
} | ||
} | ||
}; | ||
/** | ||
* Test whether a variable is a number | ||
@@ -403,3 +464,3 @@ * @param {*} value | ||
/***/ }, | ||
/* 2 */ | ||
/* 4 */ | ||
/***/ function(module, exports) { | ||
@@ -693,16 +754,16 @@ | ||
/***/ }, | ||
/* 3 */ | ||
/* 5 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
var Promise = __webpack_require__(2); | ||
var Promise = __webpack_require__(4); | ||
// determine environment | ||
var environment = __webpack_require__(4); | ||
var environment = __webpack_require__(1); | ||
// used to prevent webpack from resolving requires on node libs | ||
var node = {require: __webpack_require__(5)}; | ||
var node = {require: __webpack_require__(2)}; | ||
// get the default worker script | ||
function getDefaultWorker() { | ||
if (environment == 'browser') { | ||
if (environment.platform == 'browser') { | ||
// test whether the browser supports all features that we need | ||
@@ -752,3 +813,3 @@ if (typeof Blob === 'undefined') { | ||
if (environment == 'browser') { | ||
if (environment.platform == 'browser') { | ||
// check whether Worker is supported by the browser | ||
@@ -780,23 +841,36 @@ // Workaround for a bug in PhantomJS (Or QtWebkit): https://github.com/ariya/phantomjs/issues/14534 | ||
var me = this; | ||
this.worker.on('message', function (response) { | ||
// find the task from the processing queue, and run the tasks callback | ||
var id = response.id; | ||
var task = me.processing[id]; | ||
if (task) { | ||
// remove the task from the queue | ||
delete me.processing[id]; | ||
// test if we need to terminate | ||
if (me.terminating) { | ||
// complete worker termination if all tasks are finished | ||
me.terminate(); | ||
} | ||
// The ready message is only sent if the worker.add method is called (And the default script is not used) | ||
if (!script) { | ||
this.worker.ready = true; | ||
} | ||
// resolve the task's promise | ||
if (response.error) { | ||
task.resolver.reject(objectToError(response.error)); | ||
// queue for requests that are received before the worker is ready | ||
this.requestQueue = []; | ||
this.worker.on('message', function (response) { | ||
if (typeof response === 'string' && response === 'ready') { | ||
me.worker.ready = true; | ||
dispatchQueuedRequests(); | ||
} else { | ||
// find the task from the processing queue, and run the tasks callback | ||
var id = response.id; | ||
var task = me.processing[id]; | ||
if (task) { | ||
// remove the task from the queue | ||
delete me.processing[id]; | ||
// test if we need to terminate | ||
if (me.terminating) { | ||
// complete worker termination if all tasks are finished | ||
me.terminate(); | ||
} | ||
// resolve the task's promise | ||
if (response.error) { | ||
task.resolver.reject(objectToError(response.error)); | ||
} | ||
else { | ||
task.resolver.resolve(response.result); | ||
} | ||
} | ||
else { | ||
task.resolver.resolve(response.result); | ||
} | ||
} | ||
@@ -817,2 +891,9 @@ }); | ||
// send all queued requests to worker | ||
function dispatchQueuedRequests() | ||
{ | ||
me.requestQueue.forEach(me.worker.send.bind(me.worker)); | ||
me.requestQueue = []; | ||
} | ||
// listen for worker messages error and exit | ||
@@ -870,6 +951,7 @@ this.worker.on('error', onError); | ||
resolver.reject(new Error('Worker is terminated')); | ||
} | ||
else { | ||
} else if (this.worker.ready) { | ||
// send the request to the worker | ||
this.worker.send(request); | ||
} else { | ||
this.requestQueue.push(request); | ||
} | ||
@@ -948,44 +1030,2 @@ | ||
/***/ }, | ||
/* 4 */ | ||
/***/ function(module, exports) { | ||
// determines the JavaScript environment: browser or node | ||
module.exports = (typeof window !== 'undefined') ? 'browser' : 'node'; | ||
/***/ }, | ||
/* 5 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
var map = { | ||
"./Pool": 1, | ||
"./Pool.js": 1, | ||
"./Promise": 2, | ||
"./Promise.js": 2, | ||
"./WorkerHandler": 3, | ||
"./WorkerHandler.js": 3, | ||
"./environment": 4, | ||
"./environment.js": 4, | ||
"./generated/embeddedWorker": 6, | ||
"./generated/embeddedWorker.js": 6, | ||
"./header": 7, | ||
"./header.js": 7, | ||
"./worker": 8, | ||
"./worker.js": 8 | ||
}; | ||
function webpackContext(req) { | ||
return __webpack_require__(webpackContextResolve(req)); | ||
}; | ||
function webpackContextResolve(req) { | ||
return map[req] || (function() { throw new Error("Cannot find module '" + req + "'.") }()); | ||
}; | ||
webpackContext.keys = function webpackContextKeys() { | ||
return Object.keys(map); | ||
}; | ||
webpackContext.resolve = webpackContextResolve; | ||
module.exports = webpackContext; | ||
webpackContext.id = 5; | ||
/***/ }, | ||
/* 6 */ | ||
@@ -999,3 +1039,3 @@ /***/ function(module, exports) { | ||
*/ | ||
module.exports = "!function(r){function e(n){if(o[n])return o[n].exports;var t=o[n]={exports:{},id:n,loaded:!1};return r[n].call(t.exports,t,t.exports,e),t.loaded=!0,t.exports}var o={};return e.m=r,e.c=o,e.p=\"\",e(0)}([function(module,exports,__webpack_require__){function convertError(r){return Object.getOwnPropertyNames(r).reduce(function(e,o){return Object.defineProperty(e,o,{value:r[o],enumerable:!0})},{})}function isPromise(r){return r&&\"function\"==typeof r.then&&\"function\"==typeof r.catch}var worker={};if(\"undefined\"!=typeof self&&\"function\"==typeof postMessage&&\"function\"==typeof addEventListener)worker.on=function(r,e){addEventListener(r,function(r){e(r.data)})},worker.send=function(r){postMessage(r)};else{if(\"undefined\"==typeof process)throw new Error(\"Script must be executed as a worker\");worker.on=process.on.bind(process),worker.send=process.send.bind(process)}worker.methods={},worker.methods.run=function run(fn,args){var f=eval(\"(\"+fn+\")\");return f.apply(f,args)},worker.methods.methods=function(){return Object.keys(worker.methods)},worker.on(\"message\",function(r){try{var e=worker.methods[r.method];if(!e)throw new Error('Unknown method \"'+r.method+'\"');var o=e.apply(e,r.params);isPromise(o)?o.then(function(e){worker.send({id:r.id,result:e,error:null})}).catch(function(e){worker.send({id:r.id,result:null,error:convertError(e)})}):worker.send({id:r.id,result:o,error:null})}catch(e){worker.send({id:r.id,result:null,error:convertError(e)})}}),worker.register=function(r){if(r)for(var e in r)r.hasOwnProperty(e)&&(worker.methods[e]=r[e])},exports.add=worker.register}]);"; | ||
module.exports = "!function(r){function e(n){if(o[n])return o[n].exports;var t=o[n]={exports:{},id:n,loaded:!1};return r[n].call(t.exports,t,t.exports,e),t.loaded=!0,t.exports}var o={};return e.m=r,e.c=o,e.p=\"\",e(0)}([function(module,exports,__webpack_require__){function convertError(r){return Object.getOwnPropertyNames(r).reduce(function(e,o){return Object.defineProperty(e,o,{value:r[o],enumerable:!0})},{})}function isPromise(r){return r&&\"function\"==typeof r.then&&\"function\"==typeof r.catch}var worker={};if(\"undefined\"!=typeof self&&\"function\"==typeof postMessage&&\"function\"==typeof addEventListener)worker.on=function(r,e){addEventListener(r,function(r){e(r.data)})},worker.send=function(r){postMessage(r)};else{if(\"undefined\"==typeof process)throw new Error(\"Script must be executed as a worker\");worker.on=process.on.bind(process),worker.send=process.send.bind(process)}worker.methods={},worker.methods.run=function run(fn,args){var f=eval(\"(\"+fn+\")\");return f.apply(f,args)},worker.methods.methods=function(){return Object.keys(worker.methods)},worker.on(\"message\",function(r){try{var e=worker.methods[r.method];if(!e)throw new Error('Unknown method \"'+r.method+'\"');var o=e.apply(e,r.params);isPromise(o)?o.then(function(e){worker.send({id:r.id,result:e,error:null})}).catch(function(e){worker.send({id:r.id,result:null,error:convertError(e)})}):worker.send({id:r.id,result:o,error:null})}catch(e){worker.send({id:r.id,result:null,error:convertError(e)})}}),worker.register=function(r){if(r)for(var e in r)r.hasOwnProperty(e)&&(worker.methods[e]=r[e]);worker.send(\"ready\")},exports.add=worker.register}]);"; | ||
@@ -1159,2 +1199,3 @@ | ||
worker.register = function (methods) { | ||
if (methods) { | ||
@@ -1167,2 +1208,5 @@ for (var name in methods) { | ||
} | ||
worker.send('ready'); | ||
}; | ||
@@ -1169,0 +1213,0 @@ |
@@ -7,4 +7,4 @@ /** | ||
* | ||
* @version 2.0.0 | ||
* @date 2016-09-18 | ||
* @version 2.1.0 | ||
* @date 2016-10-11 | ||
* | ||
@@ -26,3 +26,3 @@ * @license | ||
*/ | ||
!function(r,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.workerpool=e():r.workerpool=e()}(this,function(){return function(r){function e(o){if(t[o])return t[o].exports;var n=t[o]={exports:{},id:o,loaded:!1};return r[o].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var t={};return e.m=r,e.c=t,e.p="",e(0)}([function(r,e,t){"undefined"!=typeof window;e.pool=function(r,e){var o=t(1);return new o(r,e)},e.worker=function r(e){var o=t(4);if("browser"==o){var n=new Blob([t(6)],{type:"text/javascript"}),i=window.URL.createObjectURL(n);importScripts(i)}else var r=t(8);r.add(e)},e.Promise=t(2)},function(r,e,t){function o(r,e){if("string"==typeof r?this.script=r||null:(this.script=null,e=r),e&&"maxWorkers"in e){if(!n(e.maxWorkers)||!i(e.maxWorkers)||e.maxWorkers<1)throw new TypeError("Option maxWorkers must be a positive integer number");this.maxWorkers=e.maxWorkers}else{var o=t(4),s="browser"==o?window.navigator.hardwareConcurrency||4:u.require("os").cpus().length;this.maxWorkers=Math.max(s-1,1)}this.workers=[],this.tasks=[]}function n(r){return"number"==typeof r}function i(r){return Math.round(r)==r}var s=t(2),c=t(3),u={require:t(5)};o.prototype.exec=function(r,e){if(e&&!Array.isArray(e))throw new TypeError('Array expected as argument "params"');if("string"==typeof r){var t=s.defer();return this.tasks.push({method:r,params:e,resolver:t}),this._next(),t.promise}if("function"==typeof r)return this.exec("run",[String(r),e]);throw new TypeError('Function or string expected as argument "method"')},o.prototype.proxy=function(){if(arguments.length>0)throw new Error("No arguments expected");var r=this;return this.exec("methods").then(function(e){var t={};return e.forEach(function(e){t[e]=function(){return r.exec(e,Array.prototype.slice.call(arguments))}}),t})},o.prototype._next=function(){if(this.tasks.length>0){var r=this._getWorker();if(r){var e=this,t=this.tasks.shift();t.resolver.promise.pending&&r.exec(t.method,t.params,t.resolver).then(function(){e._next()}).catch(function(){r.terminated&&e._removeWorker(r),e._next()})}}},o.prototype._getWorker=function(){for(var r=0,e=this.workers.length;r<e;r++){var t=this.workers[r];if(!t.busy())return t}return this.workers.length<this.maxWorkers?(t=new c(this.script),this.workers.push(t),t):null},o.prototype._removeWorker=function(r){r.terminate();var e=this.workers.indexOf(r);e!=-1&&this.workers.splice(e,1)},o.prototype.clear=function(r){this.workers.forEach(function(e){e.terminate(r)}),this.workers=[]},r.exports=o},function(r,e){"use strict";function t(r,e){var s=this;if(!(this instanceof t))throw new SyntaxError("Constructor must be called with the new operator");if("function"!=typeof r)throw new SyntaxError("Function parameter handler(resolve, reject) missing");var c=[],u=[];this.resolved=!1,this.rejected=!1,this.pending=!0;var a=function(r,e){c.push(r),u.push(e)};this.then=function(r,e){return new t(function(t,n){var i=r?o(r,t,n):t,s=e?o(e,t,n):n;a(i,s)},s)};var f=function(r){return s.resolved=!0,s.rejected=!1,s.pending=!1,c.forEach(function(e){e(r)}),a=function(e,t){e(r)},f=p=function(){throw new Error("Promise is already resolved")},s},p=function(r){return s.resolved=!1,s.rejected=!0,s.pending=!1,u.forEach(function(e){e(r)}),a=function(e,t){t(r)},f=p=function(){throw new Error("Promise is already resolved")},s};this.cancel=function(){return e?e.cancel():p(new n),s},this.timeout=function(r){if(e)e.timeout(r);else{var t=setTimeout(function(){p(new i("Promise timed out after "+r+" ms"))},r);s.always(function(){clearTimeout(t)})}return s},r(function(r){f(r)},function(r){p(r)})}function o(r,e,t){return function(o){try{var n=r(o);n&&"function"==typeof n.then&&"function"==typeof n.catch?n.then(e,t):e(n)}catch(r){t(r)}}}function n(r){this.message=r||"promise cancelled",this.stack=(new Error).stack}function i(r){this.message=r||"timeout exceeded",this.stack=(new Error).stack}t.prototype.catch=function(r){return this.then(null,r)},t.prototype.always=function(r){return this.then(r,r)},t.all=function(r){return new t(function(e,t){var o=r.length,n=[];o?r.forEach(function(r,i){r.then(function(r){n[i]=r,o--,0==o&&e(n)},function(r){o=0,t(r)})}):e(n)})},t.defer=function(){var r={};return r.promise=new t(function(e,t){r.resolve=e,r.reject=t}),r},n.prototype=new Error,n.prototype.constructor=Error,n.prototype.name="CancellationError",t.CancellationError=n,i.prototype=new Error,i.prototype.constructor=Error,i.prototype.name="TimeoutError",t.TimeoutError=i,r.exports=t},function(r,e,t){function o(){if("browser"==c){if("undefined"==typeof Blob)throw new Error("Blob not supported by the browser");if(!window.URL||"function"!=typeof window.URL.createObjectURL)throw new Error("URL.createObjectURL not supported by the browser");var r=new Blob([t(6)],{type:"text/javascript"});return window.URL.createObjectURL(r)}return __dirname+"/worker.js"}function n(r){for(var e=new Error(""),t=Object.keys(r),o=0;o<t.length;o++)e[t[o]]=r[t[o]];return e}function i(r){function e(r){t.terminated=!0;for(var e in t.processing)t.processing.hasOwnProperty(e)&&t.processing[e].resolver.reject(r);t.processing={}}if(this.script=r||o(),"browser"==c){if("function"!=typeof Worker&&("object"!=typeof Worker||"function"!=typeof Worker.prototype.constructor))throw new Error("Web workers not supported by the browser");this.worker=new Worker(this.script),this.worker.on=function(r,e){this.addEventListener(r,function(r){e(r.data)})},this.worker.send=function(r){this.postMessage(r)}}else this.worker=u.require("child_process").fork(this.script);var t=this;this.worker.on("message",function(r){var e=r.id,o=t.processing[e];o&&(delete t.processing[e],t.terminating&&t.terminate(),r.error?o.resolver.reject(n(r.error)):o.resolver.resolve(r.result))}),this.worker.on("error",e),this.worker.on("exit",function(){var r=new Error("Worker terminated unexpectedly");e(r)}),this.processing={},this.terminating=!1,this.terminated=!1,this.lastId=0}var s=t(2),c=t(4),u={require:t(5)};i.prototype.methods=function(){return this.exec("methods")},i.prototype.exec=function(r,e,t){t||(t=s.defer());var o=++this.lastId;this.processing[o]={id:o,resolver:t};var n={id:o,method:r,params:e};this.terminated?t.reject(new Error("Worker is terminated")):this.worker.send(n);var i=this;return t.promise.catch(function(r){(r instanceof s.CancellationError||r instanceof s.TimeoutError)&&(delete i.processing[o],i.terminate(!0))}),t.promise},i.prototype.busy=function(){return Object.keys(this.processing).length>0},i.prototype.terminate=function(r){if(r){for(var e in this.processing)this.processing.hasOwnProperty(e)&&this.processing[e].resolver.reject(new Error("Worker terminated"));this.processing={}}if(this.busy())this.terminating=!0;else{if(this.worker){if("function"==typeof this.worker.kill)this.worker.kill();else{if("function"!=typeof this.worker.terminate)throw new Error("Failed to terminate worker");this.worker.terminate()}this.worker=null}this.terminating=!1,this.terminated=!0}},r.exports=i},function(r,e){r.exports="undefined"!=typeof window?"browser":"node"},function(r,e,t){function o(r){return t(n(r))}function n(r){return i[r]||function(){throw new Error("Cannot find module '"+r+"'.")}()}var i={"./Pool":1,"./Pool.js":1,"./Promise":2,"./Promise.js":2,"./WorkerHandler":3,"./WorkerHandler.js":3,"./environment":4,"./environment.js":4,"./generated/embeddedWorker":6,"./generated/embeddedWorker.js":6,"./header":7,"./header.js":7,"./worker":8,"./worker.js":8};o.keys=function(){return Object.keys(i)},o.resolve=n,r.exports=o,o.id=5},function(r,e){r.exports='!function(r){function e(n){if(o[n])return o[n].exports;var t=o[n]={exports:{},id:n,loaded:!1};return r[n].call(t.exports,t,t.exports,e),t.loaded=!0,t.exports}var o={};return e.m=r,e.c=o,e.p="",e(0)}([function(module,exports,__webpack_require__){function convertError(r){return Object.getOwnPropertyNames(r).reduce(function(e,o){return Object.defineProperty(e,o,{value:r[o],enumerable:!0})},{})}function isPromise(r){return r&&"function"==typeof r.then&&"function"==typeof r.catch}var worker={};if("undefined"!=typeof self&&"function"==typeof postMessage&&"function"==typeof addEventListener)worker.on=function(r,e){addEventListener(r,function(r){e(r.data)})},worker.send=function(r){postMessage(r)};else{if("undefined"==typeof process)throw new Error("Script must be executed as a worker");worker.on=process.on.bind(process),worker.send=process.send.bind(process)}worker.methods={},worker.methods.run=function run(fn,args){var f=eval("("+fn+")");return f.apply(f,args)},worker.methods.methods=function(){return Object.keys(worker.methods)},worker.on("message",function(r){try{var e=worker.methods[r.method];if(!e)throw new Error(\'Unknown method "\'+r.method+\'"\');var o=e.apply(e,r.params);isPromise(o)?o.then(function(e){worker.send({id:r.id,result:e,error:null})}).catch(function(e){worker.send({id:r.id,result:null,error:convertError(e)})}):worker.send({id:r.id,result:o,error:null})}catch(e){worker.send({id:r.id,result:null,error:convertError(e)})}}),worker.register=function(r){if(r)for(var e in r)r.hasOwnProperty(e)&&(worker.methods[e]=r[e])},exports.add=worker.register}]);'},function(r,e){},function(module,exports,__webpack_require__){function convertError(r){return Object.getOwnPropertyNames(r).reduce(function(e,t){return Object.defineProperty(e,t,{value:r[t],enumerable:!0})},{})}function isPromise(r){return r&&"function"==typeof r.then&&"function"==typeof r.catch}var worker={};if("undefined"!=typeof self&&"function"==typeof postMessage&&"function"==typeof addEventListener)worker.on=function(r,e){addEventListener(r,function(r){e(r.data)})},worker.send=function(r){postMessage(r)};else{if("undefined"==typeof process)throw new Error("Script must be executed as a worker");worker.on=process.on.bind(process),worker.send=process.send.bind(process)}worker.methods={},worker.methods.run=function run(fn,args){var f=eval("("+fn+")");return f.apply(f,args)},worker.methods.methods=function(){return Object.keys(worker.methods)},worker.on("message",function(r){try{var e=worker.methods[r.method];if(!e)throw new Error('Unknown method "'+r.method+'"');var t=e.apply(e,r.params);isPromise(t)?t.then(function(e){worker.send({id:r.id,result:e,error:null})}).catch(function(e){worker.send({id:r.id,result:null,error:convertError(e)})}):worker.send({id:r.id,result:t,error:null})}catch(e){worker.send({id:r.id,result:null,error:convertError(e)})}}),worker.register=function(r){if(r)for(var e in r)r.hasOwnProperty(e)&&(worker.methods[e]=r[e])},exports.add=worker.register}])}); | ||
!function(r,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.workerpool=e():r.workerpool=e()}(this,function(){return function(r){function e(o){if(t[o])return t[o].exports;var n=t[o]={exports:{},id:o,loaded:!1};return r[o].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var t={};return e.m=r,e.c=t,e.p="",e(0)}([function(r,e,t){var o=t(1);e.pool=function(r,e){var o=t(3);return new o(r,e)},e.worker=function r(e){var r=t(8);r.add(e)},e.Promise=t(4),e.platform=o.platform,e.isMainThread=o.isMainThread,e.cpus=o.cpus},function(r,e,t){var o={require:t(2)};r.exports.platform="undefined"!=typeof Window||"undefined"!=typeof WorkerGlobalScope?"browser":"node",r.exports.isMainThread="browser"===r.exports.platform?"undefined"!=typeof Window:!process.connected,r.exports.cpus="browser"===r.exports.platform?self.navigator.hardwareConcurrency:o.require("os").cpus().length},function(r,e,t){function o(r){return t(n(r))}function n(r){return i[r]||function(){throw new Error("Cannot find module '"+r+"'.")}()}var i={"./Pool":3,"./Pool.js":3,"./Promise":4,"./Promise.js":4,"./WorkerHandler":5,"./WorkerHandler.js":5,"./environment":1,"./environment.js":1,"./generated/embeddedWorker":6,"./generated/embeddedWorker.js":6,"./header":7,"./header.js":7,"./worker":8,"./worker.js":8};o.keys=function(){return Object.keys(i)},o.resolve=n,r.exports=o,o.id=2},function(r,e,t){function o(r,e){if("string"==typeof r?this.script=r||null:(this.script=null,e=r),this.workers=[],this.tasks=[],e&&"maxWorkers"in e){if(!n(e.maxWorkers)||!i(e.maxWorkers)||e.maxWorkers<1)throw new TypeError("Option maxWorkers must be a positive integer number");this.maxWorkers=e.maxWorkers}else this.maxWorkers=Math.max((u.cpus||4)-1,1);if(e&&"minWorkers"in e){if("max"===e.minWorkers)this.minWorkers=Math.max((u.cpus||4)-1,1);else{if(!n(e.minWorkers)||!i(e.minWorkers)||e.minWorkers<0)throw new TypeError("Option minWorkers must be a positive integer number");this.minWorkers=e.minWorkers,this.maxWorkers=Math.max(this.minWorkers,this.maxWorkers)}this._ensureMinWorkers()}}function n(r){return"number"==typeof r}function i(r){return Math.round(r)==r}var s=t(4),c=t(5),u=t(1);o.prototype.exec=function(r,e){if(e&&!Array.isArray(e))throw new TypeError('Array expected as argument "params"');if("string"==typeof r){var t=s.defer();return this.tasks.push({method:r,params:e,resolver:t}),this._next(),t.promise}if("function"==typeof r)return this.exec("run",[String(r),e]);throw new TypeError('Function or string expected as argument "method"')},o.prototype.proxy=function(){if(arguments.length>0)throw new Error("No arguments expected");var r=this;return this.exec("methods").then(function(e){var t={};return e.forEach(function(e){t[e]=function(){return r.exec(e,Array.prototype.slice.call(arguments))}}),t})},o.prototype._next=function(){if(this.tasks.length>0){var r=this._getWorker();if(r){var e=this,t=this.tasks.shift();t.resolver.promise.pending&&r.exec(t.method,t.params,t.resolver).then(function(){e._next()}).catch(function(){r.terminated&&(e._removeWorker(r),e._ensureMinWorkers()),e._next()})}}},o.prototype._getWorker=function(){for(var r=0,e=this.workers.length;r<e;r++){var t=this.workers[r];if(!t.busy())return t}return this.workers.length<this.maxWorkers?(t=new c(this.script),this.workers.push(t),t):null},o.prototype._removeWorker=function(r){r.terminate();var e=this.workers.indexOf(r);e!=-1&&this.workers.splice(e,1)},o.prototype.clear=function(r){this.workers.forEach(function(e){e.terminate(r)}),this.workers=[]},o.prototype._ensureMinWorkers=function(){if(this.minWorkers)for(var r=this.workers.length;r<this.minWorkers;r++)this.workers.push(new c(this.script))},r.exports=o},function(r,e){"use strict";function t(r,e){var s=this;if(!(this instanceof t))throw new SyntaxError("Constructor must be called with the new operator");if("function"!=typeof r)throw new SyntaxError("Function parameter handler(resolve, reject) missing");var c=[],u=[];this.resolved=!1,this.rejected=!1,this.pending=!0;var a=function(r,e){c.push(r),u.push(e)};this.then=function(r,e){return new t(function(t,n){var i=r?o(r,t,n):t,s=e?o(e,t,n):n;a(i,s)},s)};var f=function(r){return s.resolved=!0,s.rejected=!1,s.pending=!1,c.forEach(function(e){e(r)}),a=function(e,t){e(r)},f=p=function(){throw new Error("Promise is already resolved")},s},p=function(r){return s.resolved=!1,s.rejected=!0,s.pending=!1,u.forEach(function(e){e(r)}),a=function(e,t){t(r)},f=p=function(){throw new Error("Promise is already resolved")},s};this.cancel=function(){return e?e.cancel():p(new n),s},this.timeout=function(r){if(e)e.timeout(r);else{var t=setTimeout(function(){p(new i("Promise timed out after "+r+" ms"))},r);s.always(function(){clearTimeout(t)})}return s},r(function(r){f(r)},function(r){p(r)})}function o(r,e,t){return function(o){try{var n=r(o);n&&"function"==typeof n.then&&"function"==typeof n.catch?n.then(e,t):e(n)}catch(r){t(r)}}}function n(r){this.message=r||"promise cancelled",this.stack=(new Error).stack}function i(r){this.message=r||"timeout exceeded",this.stack=(new Error).stack}t.prototype.catch=function(r){return this.then(null,r)},t.prototype.always=function(r){return this.then(r,r)},t.all=function(r){return new t(function(e,t){var o=r.length,n=[];o?r.forEach(function(r,i){r.then(function(r){n[i]=r,o--,0==o&&e(n)},function(r){o=0,t(r)})}):e(n)})},t.defer=function(){var r={};return r.promise=new t(function(e,t){r.resolve=e,r.reject=t}),r},n.prototype=new Error,n.prototype.constructor=Error,n.prototype.name="CancellationError",t.CancellationError=n,i.prototype=new Error,i.prototype.constructor=Error,i.prototype.name="TimeoutError",t.TimeoutError=i,r.exports=t},function(r,e,t){function o(){if("browser"==c.platform){if("undefined"==typeof Blob)throw new Error("Blob not supported by the browser");if(!window.URL||"function"!=typeof window.URL.createObjectURL)throw new Error("URL.createObjectURL not supported by the browser");var r=new Blob([t(6)],{type:"text/javascript"});return window.URL.createObjectURL(r)}return __dirname+"/worker.js"}function n(r){for(var e=new Error(""),t=Object.keys(r),o=0;o<t.length;o++)e[t[o]]=r[t[o]];return e}function i(r){function e(r){i.terminated=!0;for(var e in i.processing)i.processing.hasOwnProperty(e)&&i.processing[e].resolver.reject(r);i.processing={}}function t(){i.requestQueue.forEach(i.worker.send.bind(i.worker)),i.requestQueue=[]}if(this.script=r||o(),"browser"==c.platform){if("function"!=typeof Worker&&("object"!=typeof Worker||"function"!=typeof Worker.prototype.constructor))throw new Error("Web workers not supported by the browser");this.worker=new Worker(this.script),this.worker.on=function(r,e){this.addEventListener(r,function(r){e(r.data)})},this.worker.send=function(r){this.postMessage(r)}}else this.worker=u.require("child_process").fork(this.script);var i=this;r||(this.worker.ready=!0),this.requestQueue=[],this.worker.on("message",function(r){if("string"==typeof r&&"ready"===r)i.worker.ready=!0,t();else{var e=r.id,o=i.processing[e];o&&(delete i.processing[e],i.terminating&&i.terminate(),r.error?o.resolver.reject(n(r.error)):o.resolver.resolve(r.result))}}),this.worker.on("error",e),this.worker.on("exit",function(){var r=new Error("Worker terminated unexpectedly");e(r)}),this.processing={},this.terminating=!1,this.terminated=!1,this.lastId=0}var s=t(4),c=t(1),u={require:t(2)};i.prototype.methods=function(){return this.exec("methods")},i.prototype.exec=function(r,e,t){t||(t=s.defer());var o=++this.lastId;this.processing[o]={id:o,resolver:t};var n={id:o,method:r,params:e};this.terminated?t.reject(new Error("Worker is terminated")):this.worker.ready?this.worker.send(n):this.requestQueue.push(n);var i=this;return t.promise.catch(function(r){(r instanceof s.CancellationError||r instanceof s.TimeoutError)&&(delete i.processing[o],i.terminate(!0))}),t.promise},i.prototype.busy=function(){return Object.keys(this.processing).length>0},i.prototype.terminate=function(r){if(r){for(var e in this.processing)this.processing.hasOwnProperty(e)&&this.processing[e].resolver.reject(new Error("Worker terminated"));this.processing={}}if(this.busy())this.terminating=!0;else{if(this.worker){if("function"==typeof this.worker.kill)this.worker.kill();else{if("function"!=typeof this.worker.terminate)throw new Error("Failed to terminate worker");this.worker.terminate()}this.worker=null}this.terminating=!1,this.terminated=!0}},r.exports=i},function(r,e){r.exports='!function(r){function e(n){if(o[n])return o[n].exports;var t=o[n]={exports:{},id:n,loaded:!1};return r[n].call(t.exports,t,t.exports,e),t.loaded=!0,t.exports}var o={};return e.m=r,e.c=o,e.p="",e(0)}([function(module,exports,__webpack_require__){function convertError(r){return Object.getOwnPropertyNames(r).reduce(function(e,o){return Object.defineProperty(e,o,{value:r[o],enumerable:!0})},{})}function isPromise(r){return r&&"function"==typeof r.then&&"function"==typeof r.catch}var worker={};if("undefined"!=typeof self&&"function"==typeof postMessage&&"function"==typeof addEventListener)worker.on=function(r,e){addEventListener(r,function(r){e(r.data)})},worker.send=function(r){postMessage(r)};else{if("undefined"==typeof process)throw new Error("Script must be executed as a worker");worker.on=process.on.bind(process),worker.send=process.send.bind(process)}worker.methods={},worker.methods.run=function run(fn,args){var f=eval("("+fn+")");return f.apply(f,args)},worker.methods.methods=function(){return Object.keys(worker.methods)},worker.on("message",function(r){try{var e=worker.methods[r.method];if(!e)throw new Error(\'Unknown method "\'+r.method+\'"\');var o=e.apply(e,r.params);isPromise(o)?o.then(function(e){worker.send({id:r.id,result:e,error:null})}).catch(function(e){worker.send({id:r.id,result:null,error:convertError(e)})}):worker.send({id:r.id,result:o,error:null})}catch(e){worker.send({id:r.id,result:null,error:convertError(e)})}}),worker.register=function(r){if(r)for(var e in r)r.hasOwnProperty(e)&&(worker.methods[e]=r[e]);worker.send("ready")},exports.add=worker.register}]);'},function(r,e){},function(module,exports,__webpack_require__){function convertError(r){return Object.getOwnPropertyNames(r).reduce(function(e,t){return Object.defineProperty(e,t,{value:r[t],enumerable:!0})},{})}function isPromise(r){return r&&"function"==typeof r.then&&"function"==typeof r.catch}var worker={};if("undefined"!=typeof self&&"function"==typeof postMessage&&"function"==typeof addEventListener)worker.on=function(r,e){addEventListener(r,function(r){e(r.data)})},worker.send=function(r){postMessage(r)};else{if("undefined"==typeof process)throw new Error("Script must be executed as a worker");worker.on=process.on.bind(process),worker.send=process.send.bind(process)}worker.methods={},worker.methods.run=function run(fn,args){var f=eval("("+fn+")");return f.apply(f,args)},worker.methods.methods=function(){return Object.keys(worker.methods)},worker.on("message",function(r){try{var e=worker.methods[r.method];if(!e)throw new Error('Unknown method "'+r.method+'"');var t=e.apply(e,r.params);isPromise(t)?t.then(function(e){worker.send({id:r.id,result:e,error:null})}).catch(function(e){worker.send({id:r.id,result:null,error:convertError(e)})}):worker.send({id:r.id,result:t,error:null})}catch(e){worker.send({id:r.id,result:null,error:convertError(e)})}}),worker.register=function(r){if(r)for(var e in r)r.hasOwnProperty(e)&&(worker.methods[e]=r[e]);worker.send("ready")},exports.add=worker.register}])}); | ||
//# sourceMappingURL=workerpool.map |
@@ -5,2 +5,12 @@ # workerpool history | ||
## 2016-10-11, version 2.1.0 | ||
- Implemented support for registering the workers methods asynchronously. | ||
This enables asynchronous initialization of workers, for example when | ||
using AMD modules. Thanks @natlibfi-arlehiko. | ||
- Implemented environment variables `platform`, `isMainThread`, and `cpus`. | ||
Thanks @natlibfi-arlehiko. | ||
- Implemented option `minWorkers`. Thanks @sergei202. | ||
## 2016-09-18, version 2.0.0 | ||
@@ -7,0 +17,0 @@ |
22
index.js
@@ -1,2 +0,2 @@ | ||
var isBrowser = (typeof window !== 'undefined'); | ||
var environment = require('./lib/environment'); | ||
@@ -19,17 +19,3 @@ /** | ||
exports.worker = function worker(methods) { | ||
var environment = require('./lib/environment'); | ||
if (environment == 'browser') { | ||
// worker is already loaded by requiring worker | ||
// use embedded worker.js | ||
var blob = new Blob([require('./lib/generated/embeddedWorker')], {type: 'text/javascript'}); | ||
var url = window.URL.createObjectURL(blob); | ||
importScripts(url); | ||
} | ||
else { | ||
// node | ||
// TODO: do not include worker in browserified library | ||
var worker = require('./lib/worker'); | ||
} | ||
var worker = require('./lib/worker'); | ||
worker.add(methods); | ||
@@ -43,1 +29,5 @@ }; | ||
exports.Promise = require('./lib/Promise'); | ||
exports.platform = environment.platform; | ||
exports.isMainThread = environment.isMainThread; | ||
exports.cpus = environment.cpus; |
@@ -1,2 +0,13 @@ | ||
// determines the JavaScript environment: browser or node | ||
module.exports = (typeof window !== 'undefined') ? 'browser' : 'node'; | ||
// used to prevent webpack from resolving requires on node libs | ||
var node = {require: require}; | ||
// determines the JavaScript platform: browser or node | ||
module.exports.platform = typeof Window !== 'undefined' || typeof WorkerGlobalScope !== 'undefined' ? 'browser' : 'node'; | ||
// determines whether the code is running in main thread or not | ||
module.exports.isMainThread = module.exports.platform === 'browser' ? typeof Window !== 'undefined' : !process.connected; | ||
// determines the number of cpus available | ||
module.exports.cpus = module.exports.platform === 'browser' | ||
? self.navigator.hardwareConcurrency | ||
: node.require('os').cpus().length; // call node.require to prevent `os` to be required when loading with AMD |
@@ -6,2 +6,2 @@ /** | ||
*/ | ||
module.exports = "!function(r){function e(n){if(o[n])return o[n].exports;var t=o[n]={exports:{},id:n,loaded:!1};return r[n].call(t.exports,t,t.exports,e),t.loaded=!0,t.exports}var o={};return e.m=r,e.c=o,e.p=\"\",e(0)}([function(module,exports,__webpack_require__){function convertError(r){return Object.getOwnPropertyNames(r).reduce(function(e,o){return Object.defineProperty(e,o,{value:r[o],enumerable:!0})},{})}function isPromise(r){return r&&\"function\"==typeof r.then&&\"function\"==typeof r.catch}var worker={};if(\"undefined\"!=typeof self&&\"function\"==typeof postMessage&&\"function\"==typeof addEventListener)worker.on=function(r,e){addEventListener(r,function(r){e(r.data)})},worker.send=function(r){postMessage(r)};else{if(\"undefined\"==typeof process)throw new Error(\"Script must be executed as a worker\");worker.on=process.on.bind(process),worker.send=process.send.bind(process)}worker.methods={},worker.methods.run=function run(fn,args){var f=eval(\"(\"+fn+\")\");return f.apply(f,args)},worker.methods.methods=function(){return Object.keys(worker.methods)},worker.on(\"message\",function(r){try{var e=worker.methods[r.method];if(!e)throw new Error('Unknown method \"'+r.method+'\"');var o=e.apply(e,r.params);isPromise(o)?o.then(function(e){worker.send({id:r.id,result:e,error:null})}).catch(function(e){worker.send({id:r.id,result:null,error:convertError(e)})}):worker.send({id:r.id,result:o,error:null})}catch(e){worker.send({id:r.id,result:null,error:convertError(e)})}}),worker.register=function(r){if(r)for(var e in r)r.hasOwnProperty(e)&&(worker.methods[e]=r[e])},exports.add=worker.register}]);"; | ||
module.exports = "!function(r){function e(n){if(o[n])return o[n].exports;var t=o[n]={exports:{},id:n,loaded:!1};return r[n].call(t.exports,t,t.exports,e),t.loaded=!0,t.exports}var o={};return e.m=r,e.c=o,e.p=\"\",e(0)}([function(module,exports,__webpack_require__){function convertError(r){return Object.getOwnPropertyNames(r).reduce(function(e,o){return Object.defineProperty(e,o,{value:r[o],enumerable:!0})},{})}function isPromise(r){return r&&\"function\"==typeof r.then&&\"function\"==typeof r.catch}var worker={};if(\"undefined\"!=typeof self&&\"function\"==typeof postMessage&&\"function\"==typeof addEventListener)worker.on=function(r,e){addEventListener(r,function(r){e(r.data)})},worker.send=function(r){postMessage(r)};else{if(\"undefined\"==typeof process)throw new Error(\"Script must be executed as a worker\");worker.on=process.on.bind(process),worker.send=process.send.bind(process)}worker.methods={},worker.methods.run=function run(fn,args){var f=eval(\"(\"+fn+\")\");return f.apply(f,args)},worker.methods.methods=function(){return Object.keys(worker.methods)},worker.on(\"message\",function(r){try{var e=worker.methods[r.method];if(!e)throw new Error('Unknown method \"'+r.method+'\"');var o=e.apply(e,r.params);isPromise(o)?o.then(function(e){worker.send({id:r.id,result:e,error:null})}).catch(function(e){worker.send({id:r.id,result:null,error:convertError(e)})}):worker.send({id:r.id,result:o,error:null})}catch(e){worker.send({id:r.id,result:null,error:convertError(e)})}}),worker.register=function(r){if(r)for(var e in r)r.hasOwnProperty(e)&&(worker.methods[e]=r[e]);worker.send(\"ready\")},exports.add=worker.register}]);"; |
@@ -1,7 +0,5 @@ | ||
var Promise = require('./Promise'), | ||
WorkerHandler = require('./WorkerHandler'); | ||
var Promise = require('./Promise'); | ||
var WorkerHandler = require('./WorkerHandler'); | ||
var environment = require('./environment'); | ||
// used to prevent webpack from resolving requires on node libs | ||
var node = {require: require}; | ||
/** | ||
@@ -22,2 +20,5 @@ * A pool to manage workers | ||
this.workers = []; // queue with all workers | ||
this.tasks = []; // queue with tasks awaiting execution | ||
// configuration | ||
@@ -31,13 +32,17 @@ if (options && 'maxWorkers' in options) { | ||
else { | ||
var environment = require('./environment'); | ||
this.maxWorkers = Math.max((environment.cpus || 4) - 1, 1); | ||
} | ||
var numCPUs = (environment == 'browser') | ||
? (window.navigator.hardwareConcurrency || 4) | ||
: node.require('os').cpus().length; // call node.require to prevent `os` to be required when loading with AMD | ||
this.maxWorkers = Math.max(numCPUs - 1, 1); | ||
if (options && 'minWorkers' in options) { | ||
if(options.minWorkers==='max') { | ||
this.minWorkers = Math.max((environment.cpus || 4) - 1, 1); | ||
} else { | ||
if (!isNumber(options.minWorkers) || !isInteger(options.minWorkers) || options.minWorkers < 0) { | ||
throw new TypeError('Option minWorkers must be a positive integer number'); | ||
} | ||
this.minWorkers = options.minWorkers; | ||
this.maxWorkers = Math.max(this.minWorkers, this.maxWorkers); // in case minWorkers is higher than maxWorkers | ||
} | ||
this._ensureMinWorkers(); | ||
} | ||
this.workers = []; // queue with all workers | ||
this.tasks = []; // queue with tasks awaiting execution | ||
} | ||
@@ -167,13 +172,14 @@ | ||
worker.exec(task.method, task.params, task.resolver) | ||
.then(function () { | ||
me._next(); // trigger next task in the queue | ||
}) | ||
.catch(function () { | ||
// if the worker crashed and terminated, remove it from the pool | ||
if (worker.terminated) { | ||
me._removeWorker(worker); | ||
} | ||
me._next(); // trigger next task in the queue | ||
}); | ||
.then(function () { | ||
me._next(); // trigger next task in the queue | ||
}) | ||
.catch(function () { | ||
// if the worker crashed and terminated, remove it from the pool | ||
if (worker.terminated) { | ||
me._removeWorker(worker); | ||
// If minWorkers set, spin up new workers to replace the crashed ones | ||
me._ensureMinWorkers(); | ||
} | ||
me._next(); // trigger next task in the queue | ||
}); | ||
} | ||
@@ -224,3 +230,5 @@ } | ||
var index = this.workers.indexOf(worker); | ||
if (index != -1) this.workers.splice(index, 1); | ||
if (index != -1) { | ||
this.workers.splice(index, 1); | ||
} | ||
}; | ||
@@ -247,2 +255,14 @@ | ||
/** | ||
* Ensures that a minimum of minWorkers is up and running | ||
* @private | ||
*/ | ||
Pool.prototype._ensureMinWorkers = function() { | ||
if (this.minWorkers) { | ||
for(var i = this.workers.length; i < this.minWorkers; i++) { | ||
this.workers.push(new WorkerHandler(this.script)); | ||
} | ||
} | ||
}; | ||
/** | ||
* Test whether a variable is a number | ||
@@ -249,0 +269,0 @@ * @param {*} value |
@@ -123,2 +123,3 @@ /** | ||
worker.register = function (methods) { | ||
if (methods) { | ||
@@ -131,2 +132,5 @@ for (var name in methods) { | ||
} | ||
worker.send('ready'); | ||
}; | ||
@@ -133,0 +137,0 @@ |
@@ -11,3 +11,3 @@ var Promise = require('./Promise'); | ||
function getDefaultWorker() { | ||
if (environment == 'browser') { | ||
if (environment.platform == 'browser') { | ||
// test whether the browser supports all features that we need | ||
@@ -57,3 +57,3 @@ if (typeof Blob === 'undefined') { | ||
if (environment == 'browser') { | ||
if (environment.platform == 'browser') { | ||
// check whether Worker is supported by the browser | ||
@@ -85,23 +85,36 @@ // Workaround for a bug in PhantomJS (Or QtWebkit): https://github.com/ariya/phantomjs/issues/14534 | ||
var me = this; | ||
this.worker.on('message', function (response) { | ||
// find the task from the processing queue, and run the tasks callback | ||
var id = response.id; | ||
var task = me.processing[id]; | ||
if (task) { | ||
// remove the task from the queue | ||
delete me.processing[id]; | ||
// test if we need to terminate | ||
if (me.terminating) { | ||
// complete worker termination if all tasks are finished | ||
me.terminate(); | ||
} | ||
// The ready message is only sent if the worker.add method is called (And the default script is not used) | ||
if (!script) { | ||
this.worker.ready = true; | ||
} | ||
// resolve the task's promise | ||
if (response.error) { | ||
task.resolver.reject(objectToError(response.error)); | ||
// queue for requests that are received before the worker is ready | ||
this.requestQueue = []; | ||
this.worker.on('message', function (response) { | ||
if (typeof response === 'string' && response === 'ready') { | ||
me.worker.ready = true; | ||
dispatchQueuedRequests(); | ||
} else { | ||
// find the task from the processing queue, and run the tasks callback | ||
var id = response.id; | ||
var task = me.processing[id]; | ||
if (task) { | ||
// remove the task from the queue | ||
delete me.processing[id]; | ||
// test if we need to terminate | ||
if (me.terminating) { | ||
// complete worker termination if all tasks are finished | ||
me.terminate(); | ||
} | ||
// resolve the task's promise | ||
if (response.error) { | ||
task.resolver.reject(objectToError(response.error)); | ||
} | ||
else { | ||
task.resolver.resolve(response.result); | ||
} | ||
} | ||
else { | ||
task.resolver.resolve(response.result); | ||
} | ||
} | ||
@@ -122,2 +135,9 @@ }); | ||
// send all queued requests to worker | ||
function dispatchQueuedRequests() | ||
{ | ||
me.requestQueue.forEach(me.worker.send.bind(me.worker)); | ||
me.requestQueue = []; | ||
} | ||
// listen for worker messages error and exit | ||
@@ -175,6 +195,7 @@ this.worker.on('error', onError); | ||
resolver.reject(new Error('Worker is terminated')); | ||
} | ||
else { | ||
} else if (this.worker.ready) { | ||
// send the request to the worker | ||
this.worker.send(request); | ||
} else { | ||
this.requestQueue.push(request); | ||
} | ||
@@ -181,0 +202,0 @@ |
{ | ||
"name": "workerpool", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "Offload tasks to a pool of workers on node.js and in the browser", | ||
@@ -5,0 +5,0 @@ "homepage": "https://github.com/josdejong/workerpool", |
@@ -121,3 +121,3 @@ # workerpool | ||
}); | ||
// or run registered functions on the worker via a proxy: | ||
@@ -133,3 +133,22 @@ pool.proxy() | ||
Worker can also initialize asynchronously: | ||
**myAsyncWorker.js** | ||
```js | ||
define(['workerpool/dist/workerpool'], function(workerpool) { | ||
// a deliberately inefficient implementation of the fibonacci sequence | ||
function fibonacci(n) { | ||
if (n < 2) return n; | ||
return fibonacci(n - 2) + fibonacci(n - 1); | ||
} | ||
// create a worker and register public functions | ||
workerpool.worker({ | ||
fibonacci: fibonacci | ||
}); | ||
}); | ||
``` | ||
## Examples | ||
@@ -157,3 +176,4 @@ | ||
The following options are available: | ||
- `maxWorkers: number`. The default number of workers number of CPU's minus one. When the number of CPU's could not be determined (for example in older browsers), `maxWorkers` is 3 by default. | ||
- `minWorkers: number | 'max'`. The minimum number of workers that must be initialized and kept available. Setting this to `'max'` will create `maxWorkers` default workers (see below). | ||
- `maxWorkers: number`. The default number of maxWorkers is the number of CPU's minus one. When the number of CPU's could not be determined (for example in older browsers), `maxWorkers` is set to 3. | ||
@@ -272,8 +292,13 @@ A worker pool contains the following functions: | ||
### Utilities | ||
Following properties are available for convenience: | ||
- **platform**: The Javascript platform. Either *node* or *browser* | ||
- **isMainThread**: Whether the code is running in main thread or not (Workers) | ||
- **cpus**: The number of CPUs/cores available | ||
## Roadmap | ||
- Implement a property `minWorkers`, to ensure a minimum number of workers | ||
always up and running. | ||
- Implement functions for parallel processing: `map`, `reduce`, `forEach`, | ||
@@ -283,3 +308,3 @@ `filter`, `some`, `every`, ... | ||
fallback to processing tasks in the main application. | ||
- Implement session support: be able to handle a series of related tasks by a | ||
- Implement session support: be able to handle a series of related tasks by a | ||
single worker, which can keep a state for the session. | ||
@@ -286,0 +311,0 @@ |
@@ -380,2 +380,37 @@ var assert = require('assert'), | ||
it('should throw an error on invalid type or number of minWorkers', function () { | ||
assert.throws(function () { | ||
new Pool({minWorkers: 'a string'}); | ||
}, TypeError); | ||
assert.throws(function () { | ||
new Pool({minWorkers: 2.5}); | ||
}, TypeError); | ||
assert.throws(function () { | ||
new Pool({maxWorkers: -1}); | ||
}, TypeError); | ||
}); | ||
it('should create number of cpus minus one when minWorkers set to \'max\'', function () { | ||
var pool = new Pool({minWorkers:'max'}); | ||
var cpus = require('os').cpus(); | ||
assert.equal(pool.workers.length, cpus.length - 1); | ||
pool.clear(); | ||
}); | ||
it('should increase maxWorkers to match minWorkers', function () { | ||
var pool = new Pool({minWorkers: 16}); | ||
for(var i=0;i<20;i++) pool.exec(add, [i, i*2]); | ||
assert.equal(pool.minWorkers, 16); | ||
assert.equal(pool.maxWorkers, 16); | ||
assert.equal(pool.workers.length, 16); | ||
assert.equal(pool.tasks.length, 4); | ||
pool.clear(); | ||
}); | ||
}); | ||
@@ -446,2 +481,2 @@ | ||
}); | ||
}); |
@@ -153,2 +153,13 @@ var assert = require('assert'), | ||
it('should handle the asynchronous initialization of a worker', function (done) { | ||
var handler = new WorkerHandler(__dirname + '/workers/async.js'); | ||
handler.exec('add', [2, 4]) | ||
.then(function (result) { | ||
assert.equal(result, 6); | ||
done(); | ||
}); | ||
}); | ||
it('should cancel a task', function (done) { | ||
@@ -155,0 +166,0 @@ var handler = new WorkerHandler(); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
165774
34
3502
372