Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web workers without a separate Javascript file?

As far as I can tell, web workers need to be written in a separate JavaScript file, and called like this:

new Worker('longrunning.js') 

I'm using the closure compiler to combine and minify all my JavaScript source code, and I'd rather not have to have my workers in separate files for distribution. Is there some way to do this?

new Worker(function() {     //Long-running work here }); 

Given that first-class functions are so crucial to JavaScript, why does the standard way to do background work have to load a whole other JavaScript file from the web server?

like image 515
Ben Dilts Avatar asked Mar 23 '11 16:03

Ben Dilts


People also ask

Do web workers run on a separate thread?

Web workers let you build background threads separate from the main execution thread, where the logic of the user interface is usually executed.

Which essential JavaScript features does web workers do not have access to?

Since web workers are in external files, they do not have access to the following JavaScript objects: The window object. The document object. The parent object.

Where should you place JavaScript code to run in the context of a web worker?

You can run whatever code you like inside the worker thread, with some exceptions. For example, you can't directly manipulate the DOM from inside a worker, or use some default methods and properties of the window object.

Why use JavaScript separate files?

You should put your JS code in a separate file because this makes it easier to test and develop. The question of how you serve the code is a different matter. Serving the HTML and the JS separately has the advantage that a client can cache the JS.


2 Answers

http://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers

What if you want to create your worker script on the fly, or create a self-contained page without having to create separate worker files? With Blob(), you can "inline" your worker in the same HTML file as your main logic by creating a URL handle to the worker code as a string


Full example of BLOB inline worker:

<!DOCTYPE html> <script id="worker1" type="javascript/worker">   // This script won't be parsed by JS engines because its type is javascript/worker.   self.onmessage = function(e) {     self.postMessage('msg from worker');   };   // Rest of your worker code goes here. </script> <script>   var blob = new Blob([     document.querySelector('#worker1').textContent   ], { type: "text/javascript" })    // Note: window.webkitURL.createObjectURL() in Chrome 10+.   var worker = new Worker(window.URL.createObjectURL(blob));   worker.onmessage = function(e) {     console.log("Received: " + e.data);   }   worker.postMessage("hello"); // Start the worker. </script>
like image 56
vsync Avatar answered Sep 17 '22 14:09

vsync


The html5rocks solution of embedding the web worker code in HTML is fairly horrible.
And a blob of escaped JavaScript-as-a-string is no better, not least because it complicates work-flow (Closure compiler can't operate on strings).

Personally I really like the toString methods, but @dan-man THAT regex!

My preferred approach:

// Build a worker from an anonymous function body var blobURL = URL.createObjectURL( new Blob([ '(',  function(){     //Long-running work here }.toString(),  ')()' ], { type: 'application/javascript' } ) ),  worker = new Worker( blobURL );  // Won't be needing this anymore URL.revokeObjectURL( blobURL ); 

Support is the intersection of these three tables:

  • http://caniuse.com/#feat=webworkers
  • http://caniuse.com/#feat=blobbuilder
  • http://caniuse.com/#feat=bloburls

This won't work for a SharedWorker however, because the URL must be an exact match, even if the optional 'name' parameter matches. For a SharedWorker, you'll need a separate JavaScript file.


2015 update - The ServiceWorker singularity arrives

Now there's an even more powerful way of solving this problem. Again, store the worker code as a function, (rather than a static string) and convert using .toString(), then insert the code into CacheStorage under a static URL of your choice.

// Post code from window to ServiceWorker... navigator.serviceWorker.controller.postMessage(  [ '/my_workers/worker1.js', '(' + workerFunction1.toString() + ')()' ] );  // Insert via ServiceWorker.onmessage. Or directly once window.caches is exposed caches.open( 'myCache' ).then( function( cache ) {  cache.put( '/my_workers/worker1.js',   new Response( workerScript, { headers: {'content-type':'application/javascript'}})  ); }); 

There are two possible fall-backs. ObjectURL as above, or more seamlessly, put a real JavaScript file at /my_workers/worker1.js

Advantages of this approach are:

  1. SharedWorkers can also be supported.
  2. Tabs can share a single cached copy at a fixed address. The blob approach proliferates random objectURLs for every tab.
like image 43
Adria Avatar answered Sep 19 '22 14:09

Adria