public
Last active

BuildBridgedWorker is a function that makes communication between worker and main thread simple. It also means you can keep all related code in the same file, which is convenient.

  • Download Gist
bridged-worker.js
JavaScript
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
/* EXAMPLE:
var workerCode = function(){
"use strict;" //this will become the first line of the worker
CalculateSomething(a,b,c,d){
var v = a+b+c+d; //trivial calculation
main.DisplayResult(v,"hello");
}
CalculateSomethingBig(buff,d){
var v = new Uint32Array(buff);
for(var i=0;i<v.length;i++)
v[i] /= d;
main.PlotFraction(v.buffer,"done",0,2,"world",[v.buffer]); //the buffer is fully transfered to the main thread (google "transferable objects javascript")
}
//the BuildBridgedWorker will add some extra code on the end to form the complete worker code
}
var DisplayResult = function(val,str){
// do something here
}
var PlotFraction = function(buffer,str1,p1,p2,str2){
// do something here
}
var theWorker = BuildBridgedWorker( workerCode,
["CalculateSomething","CalculateSomethingBig*"], //note asterisk indicating ArrayBuffer transfer
["DisplayResult", "PlotFraction*"],
[DisplayResult,PlotFraction]);
// Some example inputs
var w=9,x=100,y=0,z=2;
var v = new Uint32Array(100);
// And this is how you call the functions in the worker...
theWorker.CalculateSomething(w,x,y,z);
theWorker.CalculateSomethingBig(v.buffer,x,[v.buffer]);
// Note that with the CalculateSomethingBig the buffer is transfered to the worker thread (and dissapears on the main thread)
*/
var BuildBridgedWorker = function(workerFunction,workerExportNames,mainExportNames,mainExportHandles){
//workerFunciton is a function, the interior of which will be turned into a string and used as a worker
//workerExportNames should be an array of string function names available to main
//mainExportNames should be an array of string function names available to worker
//mainExportHandles should be an array of the actual functions corresponding to the functions in main
//for both Names arrays, if the function name ends in an asterisk it means that the last argument passed is going to be an array of ArrayBuffers
//
//The result of all this work is that inside the worker we can call main.SomeMainFunction(thing,otherthing,more,[buffer1,buffer2])
//and in main we can call myWorker.SomeWorkerFunction(hello,world,[buffer1,buffer2])
//
var baseWorkerStr = workerFunction.toString().match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1];
var extraWorkerStr = [];
// build a string for the worker end of the worker-calls-funciton-in-main-thread operation
extraWorkerStr.push("var main = {};\n");
for(var i=0;i<mainExportNames.length;i++){
var name = mainExportNames[i];
if(name.charAt(name.length-1) == "*"){
name = name.substr(0,name.length-1);
mainExportNames[i] = name;//we need this trimmed version back in main
extraWorkerStr.push("main." + name + " = function(/* arguments */){\n var args = Array.prototype.slice.call(arguments); var buffers = args.pop(); \n self.postMessage({foo:'" + name + "', args:args},buffers)\n}; \n");
}else{
extraWorkerStr.push("main." + name + " = function(/* arguments */){\n var args = Array.prototype.slice.call(arguments); \n self.postMessage({foo:'" + name + "', args:args})\n}; \n");
}
}
// build a string for the worker end of the main-thread-calls-function-in-worker operation
var tmpStr = [];
for(var i=0;i<workerExportNames.length;i++){
var name = workerExportNames[i];
name = name.charAt(name.length-1) == "*" ? name.substr(0,name.length-1) : name;
tmpStr.push(name + ": " + name);
}
extraWorkerStr.push("var foos={" + tmpStr.join(",") + "};\n");
extraWorkerStr.push("self.onmessage = function(e){\n");
extraWorkerStr.push("if(e.data.foo in foos) \n foos[e.data.foo].apply(null, e.data.args); \n else \n throw(new Error('Main thread requested function ' + e.data.foo + '. But it is not available.'));\n");
extraWorkerStr.push("\n};\n");
var fullWorkerStr = baseWorkerStr + "\n\n/*==== STUFF ADDED BY BuildBridgeWorker ==== */\n\n" + extraWorkerStr.join("");
 
// create the worker
var url = window.URL.createObjectURL(new Blob([fullWorkerStr],{type:'text/javascript'}));
var theWorker = new Worker(url);
// buid a funcion for the main part of worker-calls-function-in-main-thread operation
theWorker.onmessage = function(e){
var fooInd = mainExportNames.indexOf(e.data.foo);
if(fooInd != -1)
mainExportHandles[fooInd].apply(null, e.data.args);
else
throw(new Error("Worker requested function " + e.data.foo + ". But it is not available."));
}
// build an array of functions for the main part of main-thread-calls-function-in-worker operation
var ret = {blobURL: url};//this is useful to know for debugging if you have loads of bridged workers in blobs with random names
var makePostMessageForFunction = function(name,hasBuffers){
if(hasBuffers)
return function(/*args...,[ArrayBuffer,..]*/){var args = Array.prototype.slice.call(arguments); var buffers = args.pop(); theWorker.postMessage({foo:name,args:args},buffers);}
else
return function(/*args...*/){var args = Array.prototype.slice.call(arguments); theWorker.postMessage({foo:name,args:args});};
}
for(var i=0;i<workerExportNames.length;i++){
var name = workerExportNames[i];
if(name.charAt(name.length-1) == "*"){
name = name.substr(0,name.length-1);
ret[name] = makePostMessageForFunction(name,true);
}else{
ret[name] = makePostMessageForFunction(name,false);
}
}
return ret; //we return an object which lets the main thread call the worker. The object will take care of the communication in the other direction.
}

I'm using this function to calculate temporal autocorrelations of neuronal activity in a webworker - see temporalcorr.js in github.com/d1manson/waveform.

It would be convenient if this was published on npm or bower. Mind if I do?

blittle - sure, go for it. Maybe tidy it up a bit before hand.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.