Skip to content

Instantly share code, notes, and snippets.

@evillemez
Created October 16, 2016 01:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evillemez/ee9791a7cc4d8a343358d9dbb08cf659 to your computer and use it in GitHub Desktop.
Save evillemez/ee9791a7cc4d8a343358d9dbb08cf659 to your computer and use it in GitHub Desktop.
This was an old hacky attempt at a promise-based API for running any function in a web worker. Never got around to fleshing it out.
//Code for async web worker
var fibonacciWorkerTask = function(iterations) {
var first,second,add;
for (var i=0; i<iterations; i++) {
if (i === 0) {
first = 1;
second = 2;
}
if (first+second > Number.MAX_VALUE) {
throw new Error('Exceeded maximum value of ['+Number.MAX_VALUE+'].');
}
add = first + second;
first = second;
second = add;
task.progress(i / iterations * 100);
}
task.progress(100);
task.finish(add);
};
angular.module('job', [])
.controller('JobController', function($scope) {
//create a job
var fibonacci = WorkerTask.create(fibonacciWorkerTask);
$scope.result = null;
$scope.progress = null;
$scope.getStatus = function() {
return fibonacci.isRunning() ? 'running' : 'not running';
};
//run the job
fibonacci.run(2000)
.then(function(res) {
$scope.$apply(function() {
$scope.result = res;
});
})
.catch(function(err) {
$scope.$apply(function() {
$scope.result = 'ERROR: ' + err;
});
throw new Error(err);
})
.progress(function(percent) {
$scope.$apply(function() {
$scope.progress = percent;
});
})
.finally(function() {
console.log("Done working!");
})
.done();
});
<!doctype html>
<html>
<head>
<script src='bower_components/angularjs/angular.js'></script>
<script src='bower_components/q/q.js'></script>
<script src='worker-task.js'></script>
<script src='example.js'></script>
</head>
<body ng-app='job'>
<h1>An example job...</h1>
<div id='job' ng-controller='JobController'>
<p>The job is currently... {{getStatus()}}</p>
<p>Progress: {{progress}}%</p>
<p>And the result is: {{result}}</p>
</div>
</body>
</html>
(function() {
var workerTaskContent = (function() {
return function (e) {
var cmd = e.data.cmd;
var input = e.data.input;
this.task = {
finish: function(result) { postMessage({cmd: 'finish', data: result}); },
progress: function(progress) { postMessage({cmd: 'progress', data: progress}); },
error: function(error) { postMessage({cmd: 'error', data: error}); }
};
//TODO: consider this.on(); for registering specific command handlers.
var __taskFunc = (function() { return /*INSERT*/ })();
switch (cmd) {
case 'run':
try {
var result = __taskFunc(input);
if(result) { task.finish(result); }
} catch (e) {
task.error(e.message);
}
break;
}
};
})().toString();
function WorkerTask(taskFunc, timeout, autoTerminate) {
this._deferred = Q.defer();
this._taskFunc = taskFunc;
this._timeout = (timeout === null) ? 0 : timeout;
this._autoTerminate = (autoTerminate === true) ? true : false;
this._running = false;
this._objUrl = null;
this._worker = null;
//convert worker logic to string, injecting the passed function as a string
var workerString = "onmessage = " + workerTaskContent.replace("/*INSERT*/", taskFunc.toString());
//setup the worker
this._objUrl = window.URL.createObjectURL(new Blob([workerString]));
this._worker = new Worker(this._objUrl);
//setup handler for worker responses
var self = this;
this._worker.onmessage = function(e) {
var cmd = e.data.cmd;
var data = e.data.data;
switch (cmd) {
case 'finish':
self._running = false;
self._deferred.resolve(data);
if(self._autoTerminate) {
self._worker.terminate();
window.URL.revokeObjectURL(self._objUrl);
}
break;
case 'progress':
self._deferred.notify(data);
break;
case 'error':
self._running = false;
self._deferred.reject(data);
if(self._autoTerminate) {
self._worker.terminate();
window.URL.revokeObjectURL(self._objUrl);
}
break;
}
};
}
WorkerTask.prototype = {
isRunning: function () { return this._running; },
getWorker: function() { return this._worker; },
terminate: function() {
this._worker.terminate();
window.URL.revokeObjectURL(this._objUrl);
},
run: function(input, transferables) {
this._running = true;
//TODO: implement transferable objects
this._worker.postMessage({cmd: 'run', input: input});
//TODO: implement timeout tracking
return this._deferred.promise;
}
};
WorkerTask.create = function(taskFunc) {
return new WorkerTask(taskFunc);
};
//TODO: env check
this.WorkerTask = WorkerTask;
})(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment