How to create a Web Worker from a string

JavascriptWeb WorkerData Uri

Javascript Problem Overview


How can I use create a Web worker from a string (which is supplied via a POST request)?

One way I can think of, but I'm not sure how to implement it, is by creating a data-URI from the server response, and passing that to the Worker constructor, but I've heard that some browsers don't allow this, because of the same origin policy.

MDN states the uncertainty about the origin policy around data URI's:

> Note: The URI passed as parameter of the Worker constructor must obey the same-origin policy. There is currently disagreement among browsers vendors on whether data URIs are of the same-origin or not; Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0) and later do allow data URIs as a valid script for workers. Other browsers may disagree.

Here's also a post discussing it on the whatwg.

Javascript Solutions


Solution 1 - Javascript

> ###Summary > * blob: for Chrome 8+, Firefox 6+, Safari 6.0+, Opera 15+ > * data:application/javascript for Opera 10.60 - 12 > * eval otherwise (IE 10+)

URL.createObjectURL(<Blob blob>) can be used to create a Web worker from a string. The blob can be created using the BlobBuilder API deprecated or the Blob constructor.

Demo: http://jsfiddle.net/uqcFM/49/

// URL.createObjectURL
window.URL = window.URL || window.webkitURL;

// "Server response", used in all examples
var response = "self.onmessage=function(e){postMessage('Worker: '+e.data);}";

var blob;
try {
    blob = new Blob([response], {type: 'application/javascript'});
} catch (e) { // Backwards-compatibility
    window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
    blob = new BlobBuilder();
    blob.append(response);
    blob = blob.getBlob();
}
var worker = new Worker(URL.createObjectURL(blob));

// Test, used in all examples:
worker.onmessage = function(e) {
    alert('Response: ' + e.data);
};
worker.postMessage('Test');

##Compatibility Web workers are supported in the following browsers source:

  • Chrome 3
  • Firefox 3.5
  • IE 10
  • Opera 10.60
  • Safari 4

This method's support is based on the support of the Blob API and the URL.createObjectUrl method. Blob compatibility:

  • Chrome 8+ (WebKitBlobBuilder), 20+ (Blob constructor)
  • Firefox 6+ (MozBlobBuilder), 13+ (Blob constructor)
  • Safari 6+ (Blob constructor)

IE10 supports MSBlobBuilder and URL.createObjectURL. However, trying to create a Web Worker from a blob:-URL throws a SecurityError.

Opera 12 does not support URL API. Some users may have a fake version of the URL object, thanks to this hack in browser.js.

##Fallback 1: data-URI Opera supports data-URIs as an argument to the Worker constructor. Note: Do not forget to escape special characters (Such as # and %).

// response as defined in the first example
var worker = new Worker('data:application/javascript,' +
                        encodeURIComponent(response) );
// ... Test as defined in the first example

Demo: http://jsfiddle.net/uqcFM/37/

##Fallback 2: Eval eval can be used as a fallback for Safari (<6) and IE 10.

// Worker-helper.js
self.onmessage = function(e) {
    self.onmessage = null; // Clean-up
    eval(e.data);
};
// Usage:
var worker = new Worker('Worker-helper.js');
// `response` as defined in the first example
worker.postMessage(response);
// .. Test as defined in the first example

Solution 2 - Javascript

I agree with the current accepted answer but often editing and managing the worker code will be hectic as its in the form of a string.

So optionally we can use the below approach where we can keep the worker as a function, and then covert to string->blob:

// function to be your worker
function workerFunction() {
    var self = this;
    self.onmessage = function(e) {
        console.log('Received input: ', e.data); // message received from main thread
        self.postMessage("Response back to main thread");
    }
}


///////////////////////////////

var dataObj = '(' + workerFunction + ')();'; // here is the trick to convert the above fucntion to string
var blob = new Blob([dataObj.replace('"use strict";', '')]); // firefox adds "use strict"; to any function which might block worker execution so knock it off

var blobURL = (window.URL ? URL : webkitURL).createObjectURL(blob, {
    type: 'application/javascript; charset=utf-8'
});


var worker = new Worker(blobURL); // spawn new worker

worker.onmessage = function(e) {
    console.log('Worker said: ', e.data); // message received from worker
};
worker.postMessage("some input to worker"); // Send data to our worker.

This is tested in IE11+ and FF and Chrome

Solution 3 - Javascript

The accepted answer is a bit complex, due to supporting backwards compatibility, so I wanted to post the same thing but simplified. Try this in your (modern) browser console:

const code = "console.log('Hello from web worker!')"
const blob = new Blob([code], {type: 'application/javascript'})
const worker = new Worker(URL.createObjectURL(blob))
// See the output in your console.

Solution 4 - Javascript

I've made an approach with most of your ideas and adding some of mine. The only thing my code needs on worker is to use 'this' to refer 'self' scope. I'm pretty sure that this is very improvable:

// Sample code
var code = function() {
    this.onmessage = function(e) {
        this.postMessage('Worker: '+e.data);
        this.postMessage('Worker2: '+e.data);
    };
};

// New thread worker code
FakeWorkerCode = function(code, worker) {
    code.call(this);
    this.worker = worker;
}
FakeWorkerCode.prototype.postMessage = function(e) {
    this.worker.onmessage({data: e});
}
// Main thread worker side
FakeWorker = function(code) {
    this.code = new FakeWorkerCode(code, this);
}
FakeWorker.prototype.postMessage = function(e) {
    this.code.onmessage({data: e});
}

// Utilities for generating workers
Utils = {
    stringifyFunction: function(func) {
        // Stringify the code
        return '(' + func + ').call(self);';
    },
    generateWorker: function(code) {
        // URL.createObjectURL
        windowURL = window.URL || window.webkitURL;   
        var blob, worker;
        var stringified = Utils.stringifyFunction(code);
        try {
            blob = new Blob([stringified], {type: 'application/javascript'});
        } catch (e) { // Backwards-compatibility
            window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
            blob = new BlobBuilder();
            blob.append(stringified);
            blob = blob.getBlob();
        }
        
        if ("Worker" in window) {
            worker = new Worker(windowURL.createObjectURL(blob));
        } else {
            worker = new FakeWorker(code);
        }
        return worker;
    }
};

// Generate worker
var worker = Utils.generateWorker(code);
// Test, used in all examples:
worker.onmessage = function(e) {
    alert('Response: ' + e.data);
};
function runWorker() {
    worker.postMessage('working fine');
}

Demo: http://jsfiddle.net/8N6aR/

Solution 5 - Javascript

Nice answer - I've been working on a similar problem today when trying to create Web Workers with fallback capabilities when they're not available (i.e. run worker script in main thread). As this thread is pertains to the topic, I thought I'd provide my solution here:

    <script type="javascript/worker">
        //WORKER FUNCTIONS
        self.onmessage = function(event) {
            postMessage('Hello, ' + event.data.name + '!');
        }
    </script>
   
    <script type="text/javascript">

        function inlineWorker(parts, params, callback) {

            var URL = (window.URL || window.webkitURL);

            if (!URL && window.Worker) {
                
                var worker = new window.Worker(URL.createObjectURL(new Blob([parts], { "type" : "text/javascript" })));

                worker.onmessage = function(event) {
                  callback(event.data);
                };

                worker.postMessage(params);
         
            } else {

                var postMessage = function(result) {
                  callback(result);
                };
                
                var self = {}; //'self' in scope of inlineWorker. 
                eval(parts); //Converts self.onmessage function string to function on self via nearest scope (previous line) - please email [email protected] if this could be tidier.
                self.onmessage({ 
                    data: params 
                });
            }
        }
         
        inlineWorker(
            document.querySelector('[type="javascript/worker"]').textContent, 
            {
                name: 'Chaps!!'
            },
            function(result) {
                document.body.innerHTML = result;
            }
        );

    </script>
</body>

Solution 6 - Javascript

Depending on your use case you can use something like

>task.js Simplified interface for getting CPU intensive code to run on all cores (node.js, and web)

A example would be

// turn blocking pure function into a worker task
const functionFromPostRequest = task.wrap('function (exampleArgument) {}');

// run task on a autoscaling worker pool
functionFromPostRequest('exampleArgumentValue').then(result => {
    // do something with result
});

Solution 7 - Javascript

Expanding on @Chanu_Sukarno's code, you can simply pass in a worker function (or string) to this function and it will execute it inside a web worker:

async function doWorkerTask(workerFunction, input, buffers) {
  // Create worker
  let fnString = '(' + workerFunction.toString().replace('"use strict";', '') + ')();';
  let workerBlob = new Blob([fnString]);
  let workerBlobURL = window.URL.createObjectURL(workerBlob, { type: 'application/javascript; charset=utf-8' });
  let worker = new Worker(workerBlobURL);

  // Run worker
  return await new Promise(function(resolve, reject) {
    worker.onmessage = function(e) { resolve(e.data); };
    worker.postMessage(input, buffers);
  });
}

Here's an example of how to use it:

function myTask() {
  self.onmessage = function(e) {
    // do stuff with `e.data`, then:
    self.postMessage("my response");
    self.close();
  }
}
let output = await doWorkerTask(myTask, input, inputBuffers);
// now you can do something with `output` (which will be equal to "my response")


In nodejs, doWorkerTask looks like this:

async function doWorkerTask(workerFunction, input, buffers) {
  let Worker = require('webworker-threads').Worker;
  let worker = new Worker(workerFunction);

  // Run worker
  return await new Promise(function(resolve, reject) {
    worker.onmessage = function(e) { resolve(e.data); };
    worker.postMessage(input, buffers);
  });
}

Solution 8 - Javascript

My solution (can be "promise"d easily...)

   function makeWorker(workerFunction, cb) {
        // Create worker
        var tplFun = "onmessage = function(e){console.log(e);	var args = Array.prototype.slice.call(e.data); var res=___.apply(this,args);postMessage(res);}"
        var fnTxt = workerFunction.toString().replace('"use strict";', '');
        var final = tplFun.replace("___", fnTxt);
        let workerBlob = new Blob([final]);
        let workerBlobURL = window.URL.createObjectURL(workerBlob, { type: 'application/javascript; charset=utf-8' });
        let worker = new Worker(workerBlobURL);
        return function () {
            var args = Array.prototype.slice.call(arguments);
            console.log(args)
            worker.postMessage(args);
            worker.onmessage = function (e) {
                console.log(e.data);
                cb(e.data);
            }
        }
    }

    function myTask(a, b, c) {
        return a * b * c;
    }

    function onresult(e) {
        alert(e);
    }
    var fn = makeWorker(myTask, onresult)
    fn(1, 2, 3);

Can be nice with "pure and slow" function !

Solution 9 - Javascript

Based on accepted answer, class to worker, interesting topic

// worker class
class SimpleWorker {

	constructor() {
		console.log("simple worker init");
	}

	onMessage(event) {
		console.log("main to worker", event.data);

		postMessage({
			type: "answer", 
			data: "data from worker"
		});

	}

}

// class to worker
const workerFromClass = workerClassRef => {

	console.log(workerClassRef.name, "to worker");

	// factory method, converted to string, used to instanciate worker
	let workerFactory = (self, workerClass) => {

		let worker = new workerClass();
		self["onmessage"] = worker.onMessage.bind(worker);

	};

	// compute worker code string
	// worker class & factory function
	let str = workerClassRef.toString() + "\n"
		+ "(" + workerFactory.toString() + ")" 
		+ "(this," + workerClassRef.name + ");"
		
	// worker code to blob
	let blob = new Blob(
		[str], 
		{type: "application/javascript"}
	);
		
	// return worker instance
	return new Worker(
		URL.createObjectURL(blob)
	);

};

// main
// create worker
let worker = workerFromClass(SimpleWorker);

// handle messages from worker
worker.addEventListener(
	"message", 
	event => console.log("worker to main", event.data)
);

// send message to worker
let message = {
	type: "question", 
	data: "data from main"
};
console.log("main to worker", message);
worker.postMessage(message);

Solution 10 - Javascript

You can get real-data from the objectURL and not just blob by changing the responseType to either "text" or "arraybuffer".

Here is a back-and-forth conversion of text/javascript to blob to objectURL back to blob or text/javascript.

if you are wondering, I'm using it to generate a web-worker with no external files
you may use it to return binary content, for example a YouTube video ;) (from the <video> tag resource attribute)

var blob = new Blob(['self.onmessage=function(e){postMessage(e)}'],{type: 'text/javascript'});   //->console: (object)   Blob {size: 42, type: "text/javascript", slice: function}

var obju = URL.createObjectURL(js_blob); //->console:  "blob:http%3A//stackoverflow.com/02e79c2b-025a-4293-be0f-f121dd57ccf7"

var xhr = new XMLHttpRequest();
xhr.open('GET', 'blob:http%3A//stackoverflow.com/02e79c2b-025a-4293-be0f-f121dd57ccf7', true);
xhr.responseType = 'text'; /* or "blob" */
xhr.onreadystatechange = function(){
  if(xhr.DONE !== xhr.readyState) return;

  console.log(xhr.response);
}
xhr.send();

/*
  responseType "blob" ->console: (object)   Blob {size: 42, type: "text/javascript", slice: function}
  responseType "text" ->console: (text)     'self.onmessage=function(e){postMessage(e)}'
*/

Solution 11 - Javascript

Use my tiny plugin https://github.com/zevero/worker-create

var worker_url = Worker.create("self.postMessage('Example post from Worker');");
var worker = new Worker(worker_url);

But you may also give it a function.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbigblindView Question on Stackoverflow
Solution 1 - JavascriptRob WView Answer on Stackoverflow
Solution 2 - JavascriptChanakya VadlaView Answer on Stackoverflow
Solution 3 - JavascripttrusktrView Answer on Stackoverflow
Solution 4 - JavascriptlukelaloView Answer on Stackoverflow
Solution 5 - JavascriptChris GW GreenView Answer on Stackoverflow
Solution 6 - JavascriptChad SciraView Answer on Stackoverflow
Solution 7 - Javascriptuser993683View Answer on Stackoverflow
Solution 8 - JavascriptFabi FabiView Answer on Stackoverflow
Solution 9 - JavascriptnicopowaView Answer on Stackoverflow
Solution 10 - Javascriptuser257319View Answer on Stackoverflow
Solution 11 - JavascriptzeveroView Answer on Stackoverflow