background-worker
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -12,3 +12,3 @@ // Karma configuration | ||
// frameworks to use | ||
frameworks: [ 'mocha', 'expect', 'browserify', 'sinon' ], | ||
frameworks: [ 'mocha', 'expect', 'browserify' ], | ||
@@ -15,0 +15,0 @@ browserify: { |
{ | ||
"name": "background-worker", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "Execute tasks on Web Workers without seperate files.", | ||
@@ -23,4 +23,3 @@ "main": "index.js", | ||
"dependencies": { | ||
"bluebird": "^2.3.11", | ||
"underscore": "^1.7.0" | ||
"bluebird": "^2.3.11" | ||
}, | ||
@@ -27,0 +26,0 @@ "devDependencies": { |
@@ -5,1 +5,5 @@ background-worker | ||
Execute tasks on web Workers without seperate files. | ||
*Partially made, with <3 at:* | ||
[![Foo](http://wtw.no/gfx/wtw-logo2.png)](https://github.com/wtw-software/) |
@@ -1,3 +0,4 @@ | ||
var _ = require( 'underscore' ), | ||
Promise = require( 'bluebird' ), | ||
"use strict"; | ||
var Promise = require( 'bluebird' ), | ||
inherits = require( 'util' ) .inherits, | ||
@@ -19,11 +20,15 @@ EventEmitter = require( 'events' ).EventEmitter | ||
this.worker = null | ||
this.iframe = null | ||
this._isStarted = false | ||
this.importScripts = spec.importScripts || [] | ||
this.messagehandlers = {} | ||
this.definitions = [] | ||
this.messageId = 0 | ||
this.domain = location.protocol + "//" + location.host | ||
this.definitions = spec.definitions || [] | ||
this.domain = spec.domain || (location.protocol + "//" + location.host) | ||
this._spec = spec | ||
this._worker = null | ||
this._iframe = null | ||
this._messageId = 0 | ||
this._messagehandlers = {} | ||
this._state = BackgroundWorker.CREATED | ||
if( typeof spec === 'function' ) | ||
this.define('default', spec ) | ||
} | ||
@@ -43,62 +48,186 @@ | ||
/* | ||
* Start the worker | ||
* State Created | ||
* @static | ||
*/ | ||
BackgroundWorker.CREATED = {} | ||
/* | ||
* State Running | ||
* @static | ||
*/ | ||
BackgroundWorker.RUNNING = {} | ||
/* | ||
* State Idle | ||
* @static | ||
*/ | ||
BackgroundWorker.IDLE = {} | ||
/* | ||
* State Terminated | ||
* @static | ||
*/ | ||
BackgroundWorker.TERMINATED = {} | ||
/* | ||
* Define a command on the worker | ||
* @public | ||
* @function | ||
*/ | ||
BackgroundWorker.prototype.start = function() { | ||
if( this._isStarted ) | ||
BackgroundWorker.prototype.define = function( key, val ) { | ||
this.definitions.push({ key: key, val: val }) | ||
} | ||
/* | ||
* Run a given function defined in the BackgroundWorker | ||
* @public | ||
* @function | ||
* @param {string} command - command to run | ||
* @param {array} args - arguemnts to apply to command | ||
* @returns {Promise} | ||
*/ | ||
BackgroundWorker.prototype.run = function( command, args ) { | ||
var self, messageId, message, handler, task, worker | ||
self = this | ||
if( typeof command !== 'string' ) { | ||
command = 'default' | ||
args = Array.prototype.slice.call( self ) | ||
} | ||
if( self._state === BackgroundWorker.TERMINATED ) { | ||
throw new Error( 'Cannot call run on a Terminated BackgroundWorker' ) | ||
} | ||
if( !self._isStarted ) { | ||
start( self ) | ||
} | ||
stateChange( self, BackgroundWorker.RUNNING ) | ||
messageId = getUniqueMessageId( self ) | ||
message = { command: command, args: args, messageId: messageId } | ||
handler = {} | ||
task = new Promise(function(resolve, reject) { | ||
function setIdleThen(cb) { | ||
return function(){ | ||
stateChange( self, BackgroundWorker.IDLE ) | ||
cb.apply( self, arguments ) | ||
} | ||
} | ||
handler.resolve = setIdleThen( resolve ) | ||
handler.reject = setIdleThen( reject ) | ||
}) | ||
self._messagehandlers[ messageId ] = handler | ||
if( BackgroundWorker.hasWorkerSupport() ) { | ||
self._worker.postMessage( JSON.stringify(message) ) | ||
} | ||
else { | ||
self._iframe.contentWindow.postMessage( JSON.stringify(message), self.domain ) | ||
} | ||
return task | ||
} | ||
/* | ||
* Terminate the worker | ||
* @public | ||
* @function | ||
*/ | ||
BackgroundWorker.prototype.terminate = function() { | ||
var self | ||
self = this | ||
if( BackgroundWorker.hasWorkerSupport() ) { | ||
if( self._worker ) | ||
self._worker.terminate() | ||
} | ||
else if( self._iframe ){ | ||
self._iframe.remove() | ||
} | ||
stateChange( self, BackgroundWorker.TERMINATED ) | ||
} | ||
/* | ||
* Global reference | ||
* @private | ||
*/ | ||
var global = typeof global !== 'undefined' ? global : window | ||
/* | ||
* Start the worker | ||
* @private | ||
* @function | ||
*/ | ||
function start( self ) { | ||
if( self._isStarted ) { | ||
throw new Error( 'cannot start allready started BackgroundWorker' ) | ||
} | ||
this._isStarted = true | ||
self._isStarted = true | ||
if( BackgroundWorker.hasWorkerSupport() ) { | ||
this.setupWebWorker() | ||
setupWebWorker( self ) | ||
} | ||
else { | ||
this.setupIframe() | ||
setupIframe( self ) | ||
} | ||
return this | ||
stateChange( self, BackgroundWorker.IDLE ) | ||
return self | ||
} | ||
/* | ||
* Setup a Worker | ||
* @public | ||
* @function | ||
* Setup a Worker | ||
* @private | ||
* @function | ||
*/ | ||
BackgroundWorker.prototype.setupWebWorker = function() { | ||
this.blob = new Blob([ | ||
this.getWorkerSourcecode() | ||
function setupWebWorker( self ) { | ||
self.blob = new Blob([ | ||
getWorkerSourcecode( self ) | ||
], { type: "text/javascript" }) | ||
this.worker = new Worker( window.URL.createObjectURL(this.blob) ) | ||
self._worker = new Worker( window.URL.createObjectURL(self.blob) ) | ||
this.worker.onmessage = _.bind( this.workerOnMessageHandler, this ) | ||
this.worker.onerror = _.bind( this.workerOnErrorHandler, this ) | ||
self._worker.onmessage = function( event ) { | ||
return workerOnMessageHandler( self, event ) | ||
} | ||
self._worker.onerror = function( event ) { | ||
return workerOnErrorHandler( self, event ) | ||
} | ||
} | ||
/* | ||
* Setup a Iframe | ||
* @public | ||
* @function | ||
* Setup a Iframe | ||
* @private | ||
* @function | ||
*/ | ||
BackgroundWorker.prototype.setupIframe = function() { | ||
function setupIframe( self ) { | ||
var script, src | ||
this.iframe = document.createElement( 'iframe' ) | ||
self._iframe = document.createElement( 'iframe' ) | ||
script = document.createElement( 'script' ) | ||
if( !this.iframe.style ) this.iframe.style = {} | ||
this.iframe.style.display = 'none'; | ||
if( !self._iframe.style ) self._iframe.style = {} | ||
self._iframe.style.display = 'none'; | ||
src = "" | ||
src += "var domain = '" + this.domain + "';\n" | ||
src += "var importScripts = " + JSON.stringify(this.importScripts) + ";\n" | ||
src += "var domain = '" + self.domain + "';\n" | ||
src += "var importScripts = " + JSON.stringify(self.importScripts) + ";\n" | ||
src += "var definitions = {};\n" | ||
_.forEach(this.definitions, function( definition ) { | ||
src += " definitions['" + definition.key + "'] = " + definition.val + ";\n" | ||
}) | ||
for( var i = 0; i < self.definitions.length; i++ ) { | ||
src += " definitions['" + self.definitions[i].key + "'] = " + self.definitions[i].val + ";\n" | ||
} | ||
src += ";(" + function(){ | ||
@@ -130,3 +259,3 @@ | ||
if( data.result ) | ||
return | ||
return | ||
try { | ||
@@ -149,7 +278,7 @@ var result = definitions[data.command].apply(this, data.args); | ||
window.document.body.appendChild( this.iframe ) | ||
window.document.body.appendChild( self._iframe ) | ||
this.iframe.contentWindow.addEventListener( 'message', _.bind( this.iframeOnMessageHandler, this ) ) | ||
self._iframe.contentWindow.addEventListener( 'message', function( event ){ return iframeOnMessageHandler( self, event ) } ) | ||
this.iframe.contentDocument.body.appendChild( script ) | ||
self._iframe.contentDocument.body.appendChild( script ) | ||
@@ -159,69 +288,28 @@ } | ||
/* | ||
* Terminate the worker | ||
* @public | ||
* @function | ||
* Get a uniqie messageid to identify a worker message transaction | ||
* @private | ||
* @function | ||
* @returns {int} | ||
*/ | ||
BackgroundWorker.prototype.terminate = function() { | ||
if( BackgroundWorker.hasWorkerSupport() ) { | ||
if( !this.worker ) | ||
throw new Error('BackgroundWorker has no worker to terminate') | ||
return this.worker.terminate() | ||
} | ||
else if( this.iframe ){ | ||
this.iframe.remove() | ||
} | ||
function getUniqueMessageId( self ) { | ||
return self._messageId++ | ||
} | ||
/* | ||
* Get a uniqie messageid to identify a worker message transaction | ||
* @public | ||
* @function | ||
* @returns {int} | ||
* Change state of BackgroundWorker and trigger event if it differs from old | ||
* @private | ||
* @function | ||
*/ | ||
BackgroundWorker.prototype.getUniqueMessageId = function() { | ||
return this.messageId++ | ||
} | ||
function stateChange( self, newstate ) { | ||
var oldstate | ||
/* | ||
* Define a command on the worker | ||
* @public | ||
* @function | ||
*/ | ||
BackgroundWorker.prototype.define = function( key, val ) { | ||
this.definitions.push({ key: key, val: val }) | ||
} | ||
oldstate = self._state | ||
self._state = newstate | ||
/* | ||
* Run a given function defined in the BackgroundWorker | ||
* @public | ||
* @function | ||
* @param {string} command - command to run | ||
* @param {array} args - arguemnts to apply to command | ||
* @param {function} calback | ||
* @returns {AsyncInterface} | ||
*/ | ||
BackgroundWorker.prototype.run = function( command, args, callback ) { | ||
var messageId, message, handler, task, worker | ||
messageId = this.getUniqueMessageId() | ||
message = { command: command, args: args, messageId: messageId } | ||
handler = {} | ||
task = new Promise(function(resolve, reject) { | ||
handler.resolve = resolve | ||
handler.reject = reject | ||
}) | ||
this.messagehandlers[ messageId ] = handler | ||
if( BackgroundWorker.hasWorkerSupport() ) { | ||
this.worker.postMessage( JSON.stringify(message) ) | ||
if( oldstate !== newstate ) { | ||
self.emit( 'statechange:' + newstate ) | ||
return true | ||
} | ||
else { | ||
this.iframe.contentWindow.postMessage( JSON.stringify(message), this.domain ) | ||
} | ||
return task | ||
return false | ||
} | ||
@@ -235,3 +323,3 @@ | ||
*/ | ||
BackgroundWorker.prototype.workerOnMessageHandler = function( event ) { | ||
function workerOnMessageHandler( self, event ) { | ||
var data, messagehandler | ||
@@ -241,6 +329,6 @@ | ||
messagehandler = this.messagehandlers[ data.messageId ] | ||
messagehandler = self._messagehandlers[ data.messageId ] | ||
if( data.exception ) | ||
return messagehandler.reject( this.createExceptionFromMessage( data.exception ) ) | ||
return messagehandler.reject( createExceptionFromMessage( self, data.exception ) ) | ||
@@ -256,3 +344,3 @@ messagehandler.resolve( data.result ) | ||
*/ | ||
BackgroundWorker.prototype.iframeOnMessageHandler = function( event ) { | ||
function iframeOnMessageHandler( self, event ) { | ||
var data, messagehandler | ||
@@ -264,6 +352,6 @@ | ||
messagehandler = this.messagehandlers[ data.messageId ] | ||
messagehandler = self._messagehandlers[ data.messageId ] | ||
if( data.exception ) | ||
return messagehandler.reject( this.createExceptionFromMessage( data.exception ) ) | ||
return messagehandler.reject( createExceptionFromMessage( self, data.exception ) ) | ||
@@ -277,3 +365,3 @@ messagehandler.resolve( data.result ) | ||
* Create a exception by an obect describing it | ||
* @public | ||
* @private | ||
* @function | ||
@@ -285,7 +373,7 @@ * @param {object} exception | ||
*/ | ||
BackgroundWorker.prototype.createExceptionFromMessage = function( exception ) { | ||
function createExceptionFromMessage( self, exception ) { | ||
var type, message | ||
try { | ||
type = typeof eval( exception.type ) == 'function' ? eval( exception.type ) : Error | ||
type = typeof global[exception.type] == 'function' ? global[exception.type] : Error | ||
} | ||
@@ -303,7 +391,7 @@ catch( exception ) { | ||
* Handle worker error | ||
* @public | ||
* @private | ||
* @function | ||
* @event | ||
*/ | ||
BackgroundWorker.prototype.workerOnErrorHandler = function( event ) { | ||
function workerOnErrorHandler( self, event ) { | ||
var message, error, errorType, errorMessage | ||
@@ -317,4 +405,4 @@ | ||
try { | ||
errorType = typeof eval(error[1]) == 'function' ? eval(error[1]) : Error | ||
errorMessage = typeof eval(error[1]) == 'function' ? error[2] : message | ||
errorType = typeof global[error[1]] == 'function' ? global[error[1]] : Error | ||
errorMessage = typeof global[error[1]] == 'function' ? error[2] : message | ||
} | ||
@@ -328,3 +416,3 @@ catch( exception ) { | ||
this.emit( 'exception', error ) | ||
self.emit( 'exception', error ) | ||
} | ||
@@ -334,7 +422,7 @@ | ||
* Get the sourcecode for this worker | ||
* @public | ||
* @private | ||
* @function | ||
* @returns {string} | ||
*/ | ||
BackgroundWorker.prototype.getWorkerSourcecode = function() { | ||
function getWorkerSourcecode( self ) { | ||
var src | ||
@@ -344,10 +432,11 @@ | ||
if( this.importScripts.length ) | ||
src += "importScripts( '" + this.importScripts.join("','") + "' );\n" | ||
if( self.importScripts.length ) { | ||
src += "importScripts( '" + self.importScripts.join("','") + "' );\n" | ||
} | ||
src += " var definitions = {};" | ||
_.forEach(this.definitions, function( definition ) { | ||
src += " definitions['" + definition.key + "'] = " + definition.val + ";" | ||
}) | ||
for( var i = 0; i < self.definitions.length; i++ ) { | ||
src += " definitions['" + self.definitions[i].key + "'] = " + self.definitions[i].val + ";\n" | ||
} | ||
@@ -354,0 +443,0 @@ src += "self.onmessage = function( event ) { " + |
@@ -6,18 +6,9 @@ var BackgroundWorker = require( '../../' ) | ||
describe( 'BackgroundWorker#start', function() { | ||
describe( 'WebWorker', function() { | ||
it( 'should throw exepction if tried to start when allready started', function() { | ||
var worker | ||
TestSharedAPI() | ||
worker = new BackgroundWorker() | ||
worker.start() | ||
expect(function(){ worker.start() }) | ||
.to.throwException() | ||
}) | ||
}) | ||
describe( 'Running in Iframe', function( done ) { | ||
describe( 'Iframe', function() { | ||
@@ -33,3 +24,13 @@ before(function() { | ||
it('Should run', function( done ) { | ||
TestSharedAPI() | ||
}) | ||
}) | ||
function TestSharedAPI(){ | ||
describe( 'BackgroundWorker#run', function(){ | ||
it('should run predefined functions', function( done ){ | ||
var worker | ||
@@ -39,9 +40,34 @@ | ||
worker.define('job', function(){ return 'ran' }.toString()) | ||
worker.define('add', function( a, b ){ return a + b }) | ||
worker.define('sub', function( a, b ){ return a - b }) | ||
worker.start() | ||
Promise.all([ | ||
worker.run('add', [1, 2]), | ||
worker.run('sub', [5, 4]) | ||
]).then(function( results ) { | ||
expect(results[0]).to.equal( 3 ) | ||
expect(results[1]).to.equal( 1 ) | ||
done() | ||
}) | ||
}) | ||
worker.run('job').then(function( res ) { | ||
expect(res).to.equal('ran') | ||
done() | ||
it('should run first argument as default function if first argument is a function to constructor', function( done ) { | ||
var worker | ||
worker = new BackgroundWorker(function() { | ||
return "default" | ||
}) | ||
worker.define('other', function(){ | ||
return "other" | ||
}) | ||
worker.run().then(function( result ) { | ||
expect( result ).to.equal( "default" ) | ||
worker.run('other').then(function( result ) { | ||
expect( result ).to.equal( "other" ) | ||
done() | ||
}) | ||
}) | ||
}) | ||
@@ -51,2 +77,42 @@ | ||
describe('BackgroundWorker#terminate', function() { | ||
it('should not throw exception of trying to run a terminated worker', function() { | ||
var worker | ||
worker = new BackgroundWorker() | ||
worker.terminate() | ||
expect(function(){ | ||
worker.run() | ||
}).to.throwException(function (e) { | ||
expect(e.message.toLowerCase().match('terminated')).to.be.ok() | ||
}); | ||
}) | ||
}) | ||
describe( 'Exceptions', function() { | ||
it('should throw correct typeof exception', function( done ) { | ||
var worker | ||
worker = new BackgroundWorker() | ||
worker.define('TypeError', function(){ throw new TypeError() }) | ||
worker.define('SyntaxError', function(){ throw new SyntaxError() }) | ||
worker.run('SyntaxError').catch(function( error ) { | ||
expect( error ).to.be.a(SyntaxError) | ||
worker.run('TypeError').catch(function( error ) { | ||
expect( error ).to.be.a(TypeError) | ||
done() | ||
}) | ||
}) | ||
}) | ||
}) | ||
it('should import scripts', function( done ) { | ||
@@ -61,7 +127,3 @@ var worker | ||
worker.start() | ||
console.log('RUN') | ||
worker.run('func').then(function( res ) { | ||
console.log('RAN') | ||
expect(res).to.equal('imported') | ||
@@ -71,5 +133,2 @@ done() | ||
}) | ||
}) | ||
}) | ||
} |
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
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
15843
1
492
9
0
- Removedunderscore@^1.7.0
- Removedunderscore@1.13.7(transitive)