315

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?

Fouad Boukredine
  • 1,037
  • 11
  • 14
Ben Dilts
  • 9,659
  • 16
  • 49
  • 82
  • 12
    It's because keeping an execution context purely threadsafe is even more crucial than first-class functions :-) – Pointy Mar 23 '11 at 16:47
  • 1
    I'm working on it (or rather on minimising the problem): [DynWorker](https://github.com/passcod/DynWorker). You can do: `var worker = new DynWorker(); worker.inject("foo", function(){...});`... – Félix Saparelli Nov 08 '11 at 03:34
  • 2
    Related: [How to create a Web Worker from a string](http://stackoverflow.com/q/10343913/938089?how-to-create-a-web-worker-from-a-string) and [Teaching Worker to accept code instead of javascript source file](http://stackoverflow.com/q/10483320/938089?teaching-worker-to-accept-code-instead-of-javascript-source-file). – Rob W May 07 '12 at 14:58
  • 1
    The OP deleted the "Teaching Worker to accept function instead of JavaScript source file" question. The answer is reposted [here](http://stackoverflow.com/a/11381924) – Rob W Jul 08 '12 at 09:44
  • I developed [task.js](https://github.com/icodeforlove/task.js) to make this much easier to do. Most of the time you're only trying to offload small locking tasks. – Chad Scira Jul 30 '16 at 19:26

27 Answers27

250

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>
Community
  • 1
  • 1
vsync
  • 87,559
  • 45
  • 247
  • 317
  • Google Chrome only solution, seems Firefox 10 will support it, i don't know about other browsers – 4esn0k Dec 01 '11 at 13:07
  • 2
    BlobBuiler [is now deprecated](https://developer.mozilla.org/docs/DOM/BlobBuilder). Use [Blob](https://developer.mozilla.org/docs/DOM/Blob) instead. Currently supported in latest Firefox/WebKit/Opera and IE10, see [compatibility tables](http://caniuse.com/#search=blob) for older browsers. – Félix Saparelli Jan 19 '13 at 09:42
  • 3
    Blob constructor might be supported in IE10, but you still cannot pass javascript to the web worker through it (not even in IE11): https://connect.microsoft.com/IE/feedback/details/801810/web-workers-from-blob-urls-in-ie-10-and-11. – jayarjo Sep 07 '14 at 20:18
  • @jayarjo - what about what Feliz said? he shows that there is support for IE10 also – vsync Sep 08 '14 at 08:04
  • My only doubt is if in this case it is really thread save. I made some test and it seems to hand the browser until it finished – albanx Oct 20 '14 at 16:12
  • 1
    @albanx -what tests? there are billion demo pages online already which shows threading doesn't hang-up the browser for years. – vsync Oct 21 '14 at 09:24
  • 2
    @albanx - would you care to at least say which esoteric browser do you use which hangs? does this demo hangs for you? http://ie.microsoft.com/testdrive/Graphics/WorkerFountains/Default.html – vsync Oct 21 '14 at 10:50
  • MDN has an example of this: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Embedded_workers – TheZ Mar 24 '16 at 23:14
  • Great piece of code! I needed a couple more things such as passing parameters and no external file so changed it a bit and put on jsfiddle @ https://jsfiddle.net/mbr9q0z5/. I am by no means a js guru so any suggestion for improvement is welcome! – Thomas Apr 02 '16 at 16:11
  • Blob support is in IE 11 now: https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/11673552-support-web-worker-creation-via-blob-urls – Sammi Jan 25 '19 at 14:43
  • I've never understood this - why is the abstraction upon files and not on classes or closures in the API. We shouldn't require any IO which would be the primary use of abstracting over files. It seems an inherent flaw in the design of the Web workers API from a user stand-point and one which is a deal breaker in terms of trying to adopt them. I always think 'why am I not using Web workers for this?' and then this post and others remind me. – alex.p Feb 01 '20 at 07:53
  • Please advise where can I access and review the source code of the `window.Worker` class. I am wondering how they fetch script content from the URL without the use of sync XMLHttpRequest that is now depreciated. Thanks! – Ωmega Feb 13 '21 at 23:17
178

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:

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.
Community
  • 1
  • 1
Adria
  • 7,508
  • 4
  • 34
  • 25
  • 4
    What would browser compatibility look like on this solution? – Ben Dilts Oct 09 '13 at 14:26
  • Can you elaborate on this solution, how does it work? What is the worker1.js? Is it a seperate js file? I am trying to use this but unable to make it work. Specifically I am trying to make it work for a SharedWorker – Yehuda Mar 15 '16 at 18:14
  • If only you could wrap it up in a useful function! – mmm Sep 27 '16 at 07:39
  • @ Ben Dilts: Browser compatibility would look like just running your code through babel: https://babeljs.io/repl/ – Jack Giffin Mar 17 '17 at 18:13
  • The standard does not gurantee that Function.prototype.toString() returns the function body as string. You should probably add a warning to the answer. – R D Mar 24 '17 at 16:51
  • When I called `URL.revokeObjectURL(blobURL)` in IE11 right after the creation of the worker, my created web worker stopped to respond without any errors or notification. There is no such issue in Chrome. I'd advise to revoke the url ONLY AFTER you've got everything you need from the web worker. – Salasar Feb 27 '19 at 10:21
  • I like this answer (the first one), but it does not allow any imports or `imporScripts` statements. How could we possibly combine this method with importing other scripts or modules *into* the worker? – Seth Lutske Oct 22 '20 at 05:59
38

You can create a single JavaScript file that is aware of its execution context and can act as both a parent script and a worker. Let's start off with a basic structure for a file like this:

(function(global) {
    var is_worker = !this.document;
    var script_path = is_worker ? null : (function() {
        // append random number and time to ID
        var id = (Math.random()+''+(+new Date)).substring(2);
        document.write('<script id="wts' + id + '"></script>');
        return document.getElementById('wts' + id).
            previousSibling.src;
    })();
    function msg_parent(e) {
        // event handler for parent -> worker messages
    }
    function msg_worker(e) {
        // event handler for worker -> parent messages
    }
    function new_worker() {
        var w = new Worker(script_path);
        w.addEventListener('message', msg_worker, false);
        return w;
    }
    if (is_worker)
        global.addEventListener('message', msg_parent, false);

    // put the rest of your library here
    // to spawn a worker, use new_worker()
})(this);

As you can see, the script contains all code for both the parent's and the worker's point of view, checking if its own individual instance is a worker with !document. The somewhat unwieldy script_path computation is used to accurately calculate the script's path relative to the parent page, as the path supplied to new Worker is relative to the parent page, not the script.

Delan Azabani
  • 73,106
  • 23
  • 158
  • 198
  • 4
    Your site appears to have vanished; do you have a new URL? – BrianFreud Jul 14 '12 at 02:00
  • 1
    This is an interesting approach. FWIW, I feature-detect Web Workers by checking for the presence of "self" (the Web Worker global object) vs "window". – pwnall May 24 '13 at 09:52
  • I've been looking into how PapaParse handles Web Workers and they seem to take this approach https://github.com/mholt/PapaParse – JP DeVries Jun 21 '15 at 16:51
  • I think testing using 'typeof importScripts !== null' can tell if script is running in worker scope. – MeTTeO Sep 15 '15 at 17:39
  • 1
    I dont understand what the previousSibling is from the script-element. Can someone explain me? – Teemoh Jun 05 '17 at 17:33
  • Check at my example in the last answer, I've just **encapsulated** everything and you just have to pass a callback and in the promise resolve you'll get the result – Fernando Carvajal Jan 26 '18 at 02:50
29

Using the Blob method, how about this for a worker factory:

var BuildWorker = function(foo){
   var str = foo.toString()
             .match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1];
   return  new Worker(window.URL.createObjectURL(
                      new Blob([str],{type:'text/javascript'})));
}

So you could use it like this...

var myWorker = BuildWorker(function(){
   //first line of worker
   self.onmessage(){....};
   //last line of worker
});

EDIT:

I've just extended this idea further to make it easier to do cross-thread communication: bridged-worker.js.

EDIT 2:

The above link is to a gist I created. Someone else later turned it into an actual repo.

dan-man
  • 2,517
  • 1
  • 22
  • 40
11

Web workers operate in entirely separate contexts as individual Program's.

This means that code cannot be moved from one context to another in object form, as they would then be able to reference objects via closures belonging to the other context.
This is especially crucial as ECMAScript is designed to be a single threaded language, and since web workers operate in separate threads, you would then have the risk of non-thread-safe operations being performed.

This again means that web workers need to be initialized with code in source form.

The spec from WHATWG says

If the origin of the resulting absolute URL is not the same as the origin of the entry script, then throw a SECURITY_ERR exception.

Thus, scripts must be external files with the same scheme as the original page: you can't load a script from a data: URL or javascript: URL, and an https: page couldn't start workers using scripts with http: URLs.

but unfortunately it doesn't really explain why one couldn't have allowed passing a string with source code to the constructor.

Sean Kinsey
  • 35,601
  • 7
  • 49
  • 68
9

Recent answer (2018)

You can use Greenlet:

Move an async function into its own thread. A simplified single-function version of Workerize.

Example:

import greenlet from 'greenlet'

const getName = greenlet(async username => {
  const url = `https://api.github.com/users/${username}`
  const res = await fetch(url)
  const profile = await res.json()
  return profile.name
})

console.log(await getName('developit'))
GG.
  • 17,726
  • 11
  • 69
  • 117
6

a better to read way for a inline worker..

    var worker_fn = function(e) 
    {
        self.postMessage('msg from worker');            
    };

    var blob = new Blob(["onmessage ="+worker_fn.toString()], { type: "text/javascript" });

    var worker = new Worker(window.URL.createObjectURL(blob));
    worker.onmessage = function(e) 
    {
       alert(e.data);
    };
    worker.postMessage("start"); 
Chris Tobba
  • 71
  • 1
  • 3
  • What I did was that I created a function with all the worker code, pass that function `toString()`, extrack the body and then put that in a Blob. Check at the last answer, I have an example – Fernando Carvajal Jan 26 '18 at 02:53
5

Taking Adria's response and putting it in a copy-pastable function which works with current Chrome and FF but not IE10 (worker from blob causes a security error).

var newWorker = function (funcObj) {
    // Build a worker from an anonymous function body
    var blobURL = URL.createObjectURL(new Blob(
        ['(', funcObj.toString(), ')()'],
        {type: 'application/javascript'}
     ));

    var worker = new Worker(blobURL);

    // Won't be needing this anymore
    URL.revokeObjectURL(blobURL);

    return worker;
}

And here's a working example http://jsfiddle.net/ubershmekel/YYzvr/

Community
  • 1
  • 1
ubershmekel
  • 9,570
  • 6
  • 62
  • 78
3

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

function blocking (exampleArgument) {
    // block thread
}

// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);

// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
    // do something with result
});
Chad Scira
  • 9,075
  • 3
  • 49
  • 48
2

Take a look at the vkThread plugin. With htis plugin you can take any function in your main code and execute it in a thread (web worker). So, you don't need to create a special "web-worker file".

http://www.eslinstructor.net/vkthread/

--Vadim

vadimk
  • 1,415
  • 15
  • 11
2

So I think we have another cool option for this now, thanks to template literals in ES6. That allows us to dispense with the extra worker function (and its weird scope) and just write the code that's intended for the worker as multiline text, much like the case where we were using to store text, but without actually needing a document or DOM to do that in. Example:

const workerScript = `
self.addEventListener('message', function(e) {
  var data = e.data;
  console.log('worker recieved: ',data);
  self.postMessage('worker added! :'+ addOne(data.value));
  self.close();//kills the worker
}, false);
`;

Here's a gist of the rest of that approach.

Note that we can pull in any extra function dependencies we want into the worker just by collecting them into an array and running .toString on each of them to reduce them down into strings as well (should work as long as they are function declarations) and then just prepending that to the script string. That way we don't have to importScripts that we might already have bundled into the scope of the code we're writing.

The only real downside to this particular version is that linters won't be able to lint the service worker code (since it's just a string), which is an advantage for the "separate worker function approach."

Dtipson
  • 1,315
  • 14
  • 20
2

A simple promisified version, Function#callAsWorker, that takes a thisArg and arguments (just like call), and returns a promise:

Function.prototype.callAsWorker = function (...args) {
    return new Promise( (resolve, reject) => {
        const code = `self.onmessage = e => self.postMessage((${this.toString()}).call(...e.data));`,
            blob = new Blob([code], { type: "text/javascript" }),
            worker = new Worker(window.URL.createObjectURL(blob));
        worker.onmessage = e => (resolve(e.data), worker.terminate());
        worker.onerror = e => (reject(e.message), worker.terminate());
        worker.postMessage(args);
    });
}

// Demo
function add(...nums) {
    return nums.reduce( (a,b) => a+b );
}
// Let the worker execute the above function, with the specified arguments
add.callAsWorker(null, 1, 2, 3).then(function (result) {
    console.log('result: ', result);
});
trincot
  • 211,288
  • 25
  • 175
  • 211
  • you should add `close()` method to close your web worker life hook. https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/close – Shahar ドーン Levi Mar 03 '19 at 07:31
  • @ShaharドーンLevi, the `close` function is deprecated. However, workers can be [terminated](https://developer.mozilla.org/en-US/docs/Web/API/Worker/terminate). I have added that now. – trincot Mar 03 '19 at 07:53
1

You can use web workers in same javascript fie using inline webworkers.

The below article will address you to easily understand the webworkers and their limitations and debugging of webworkers.

Mastering in webworkers

Tristan Forward
  • 2,411
  • 6
  • 29
  • 39
kirankumar
  • 161
  • 1
  • 2
  • 12
1

I think the better way to do this is using a Blob object, below you can see a simple example.

// create a Blob object with a worker code
var blob = new Blob(["onmessage = function(e) { postMessage('msg from worker'); }"]);

// Obtain a blob URL reference to our worker 'file'.
var blobURL = window.URL.createObjectURL(blob);

// create a Worker
var worker = new Worker(blobURL);
worker.onmessage = function(e) {
  console.log(e.data);
};
worker.postMessage("Send some Data"); 
Miguel Q.
  • 496
  • 4
  • 13
1

Try to use jThread. https://github.com/cheprasov/jThread

// You can use simple calling like this
jThread(
    function(arr){
        //... some code for Worker
        return arr;
    }
    ,function(arr){
        //... done code
    }
)( [1,2,3,4,5,6,7] ); // some params
1

here console:

var worker=new Worker(window.URL.createObjectURL(new Blob([function(){
  //Long-running work here
  postMessage('done');
}.toString().split('\n').slice(1,-1).join('\n')],{type:'text/javascript'})));

worker.addEventListener('message',function(event){
  console.log(event.data);
});
59naga
  • 111
  • 1
  • 4
1

https://developer.mozilla.org/es/docs/Web/Guide/Performance/Using_web_workers

    // Syntax: asyncEval(code[, listener])

var asyncEval = (function () {

  var aListeners = [], oParser = new Worker("data:text/javascript;charset=US-ASCII,onmessage%20%3D%20function%20%28oEvent%29%20%7B%0A%09postMessage%28%7B%0A%09%09%22id%22%3A%20oEvent.data.id%2C%0A%09%09%22evaluated%22%3A%20eval%28oEvent.data.code%29%0A%09%7D%29%3B%0A%7D");

  oParser.onmessage = function (oEvent) {
    if (aListeners[oEvent.data.id]) { aListeners[oEvent.data.id](oEvent.data.evaluated); }
    delete aListeners[oEvent.data.id];
  };


  return function (sCode, fListener) {
    aListeners.push(fListener || null);
    oParser.postMessage({
      "id": aListeners.length - 1,
      "code": sCode
    });
  };

})();
hamboy75
  • 654
  • 7
  • 17
1

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

var worker_url = Worker.createURL(function(e){
  self.postMessage('Example post from Worker'); //your code here
});
var worker = new Worker(worker_url);
zevero
  • 1,834
  • 18
  • 11
1

This is just an addition to above - I have a nice templates for testing web workers in jsFiddle. Rather than Blob it uses jsFiddles ?js api:

function workerFN() {
  self.onmessage = function(e) {
    switch(e.data.name) {
      case "" : 
      break;
      default:
        console.error("Unknown message:", e.data.name);
    }
  }
}
// This is a trick to generate real worker script that is loaded from server
var url = "/echo/js/?js="+encodeURIComponent("("+workerFN.toString()+")()");
var worker = new Worker(url);
worker.addEventListener("message", function(e) {
  switch(e.data.name) {
    case "" : 
    break;
    default:
      console.error("Unknown message:", e.data.name);
  }
})

Normal web worker and shared worker templates are available.

1

I discovered that CodePen currently does not syntax-highlight inline <script> tags that are not type="text/javascript" (or which have no type attribute).

So I devised a similar but slightly different solution using labeled blocks with break, which is the only way you can bail from a <script> tag without creating a wrapper function (which is unnecessary).

<!DOCTYPE html>
<script id="worker1">
  worker: { // Labeled block wrapper

    if (typeof window === 'object') break worker; // Bail if we're not a 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>
msanford
  • 10,127
  • 8
  • 56
  • 83
0

I use code like this, you can define your onmessage as a function other than plain text, so the editor can highlight your code and jshint works.

const worker = createWorker();

createWorker() {
    const scriptContent = getWorkerScript();
    const blob = new Blob([
        scriptContent,
    ], {
        type: "text/javascipt"
    });
    const worker = new Worker(window.URL.createObjectURL(blob));
    return worker;
}

getWorkerScript() {
    const script = {
        onmessage: function (e) {
            console.log(e);
            let result = "Hello " + e.data
            postMessage(result);
        }
    };
    let content = "";
    for (let prop in script){
        content += `${prop}=${script[prop].toString()}`;
    }
    return content;
}
Z.JC
  • 1
  • 1
  • Look at my [answer](https://stackoverflow.com/a/47804699/4612927), I just did that but I wrote a whole class for abstracting how to pass callbacks – Fernando Carvajal Jan 26 '18 at 06:01
0

Yes, it is possible, I did it using Blob files and passing a callback

I'll show you what a class I wrote does and how it manages the execution of callbacks in the background.

First you instantiate the GenericWebWorker with whatever data you'd like to pass to callback that'll be executing in the Web Worker, that includes functions you want to use, in this case a number, a date and a function called blocker

var worker = new GenericWebWorker(100, new Date(), blocker)

This blocker function will execute an infinite while for n miliseconds

function blocker (ms) {
    var now = new Date().getTime();
    while(true) {
        if (new Date().getTime() > now +ms)
            return;
    }   
}

and then you use it like this

worker.exec((num, date, fnBlocker) => {
    /*Everithing here does not block the main thread
      and this callback has access to the number, date and the blocker */
    fnBlocker(10000) //All of this run in backgrownd
    return num*10

}).then(d => console.log(d)) //Print 1000

Now, time to see the magic in the example below

/*https://github.com/fercarvo/GenericWebWorker*/
class GenericWebWorker {
    constructor(...ags) {
        this.args = ags.map(a => (typeof a == 'function') ? {type:'fn', fn:a.toString()} : a)
    }

    async exec(cb) {
        var wk_string = this.worker.toString();
        wk_string = wk_string.substring(wk_string.indexOf('{') + 1, wk_string.lastIndexOf('}'));            
        var wk_link = window.URL.createObjectURL( new Blob([ wk_string ]) );
        var wk = new Worker(wk_link);

        wk.postMessage({ callback: cb.toString(), args: this.args });
 
        var resultado = await new Promise((next, error) => {
            wk.onmessage = e => (e.data && e.data.error) ? error(e.data.error) : next(e.data);
            wk.onerror = e => error(e.message);
        })

        wk.terminate(); window.URL.revokeObjectURL(wk_link);
        return resultado
    }

    async parallel(arr, cb) {
        var res = [...arr].map(it => new GenericWebWorker(it, ...this.args).exec(cb))
        var all = await Promise.all(res)
        return all
    }

    worker() {
        onmessage = async function (e) {
            try {                
                var cb = new Function(`return ${e.data.callback}`)();
                var args = e.data.args.map(p => (p.type == 'fn') ? new Function(`return ${p.fn}`)() : p);

                try {
                    var result = await cb.apply(this, args); //If it is a promise or async function
                    return postMessage(result)

                } catch (e) { throw new Error(`CallbackError: ${e}`) }
            } catch (e) { postMessage({error: e.message}) }
        }
    }
}


function blocker (ms) {
    var now = new Date().getTime();
    while(true) {
        if (new Date().getTime() > now +ms)
            return;
    }   
}

setInterval(()=> console.log("Not blocked " + Math.random()), 1000)

console.log("\n\nstarting blocking code in Worker\n\n")

var worker = new GenericWebWorker(100, new Date(), blocker)

worker.exec((num, date, fnBlocker) => {
    fnBlocker(7000) //All of this run in backgrownd
    return num*10    
})
.then(d => console.log(`\n\nEnd of blocking code: result ${d}\n\n`)) //Print 1000
Fernando Carvajal
  • 1,395
  • 15
  • 18
0

You can place the contents of your worker.js file inside backticks (which allows a multiline string constant) and create the worker from a blob like this:

var workerScript = `
    self.onmessage = function(e) {
        self.postMessage('message from worker');
    };
    // rest of worker code goes here
`;

var worker =
    new Worker(createObjectURL(new Blob([workerScript], { type: "text/javascript" })));

This is handy if for whatever reason you don't want to have separate script tags for the worker.

samgak
  • 22,290
  • 4
  • 50
  • 73
0

Another solution is just to wrap the Worker in a function, then creating a blob invoking the function like so:

     function workerCode() {
        self.onmessage = function (e) {
          console.log("Got message from parent", e.data);
        };
        setTimeout(() => {
          self.postMessage("Message From Worker");
        }, 2000);
      }

      let blob = new Blob([
        "(" + workerCode.toString() + ")()"
      ], {type: "text/javascript"});

      // Note: window.webkitURL.createObjectURL() in Chrome 10+.
      let worker = new Worker(window.URL.createObjectURL(blob));
      worker.onmessage = function (e) {
        console.log("Received: " + e.data);
      };
      worker.postMessage("hello"); // Start the worker.
Shlomi Schwartz
  • 11,238
  • 25
  • 93
  • 155
0

One-liner for running functions in workers:

const FunctionalWorker = fn => new Worker(window.URL.createObjectURL(new Blob(["(" + fn.toString() + ")()"], {type: "text/javascript"})));

Example usage:

let fn = FunctionalWorker(() => {
    self.postMessage("hi");
});
fn.onmessage = msg => {
    console.log(msg);
};
ifbamoq
  • 156
  • 1
  • 12
0

there have been a few answers, but here is another inline version.

note: "self" argument is purely cosmetic for linting purposes, actual worker code starts after first brace, self is as normal

inlineWorker (
   "hello world",// initial message to send to worker
   function(self){
// inline worker code.
  self.onmessage = function (e) {
  
    self.postMessage("thinking...");
     for (var i=0;i<100000000;i++) {
         var r = Math.random();
     }
     self.postMessage(e.data.toUpperCase());
  }

},function(e){
  // optional message handler
  document.getElementById("log").innerHTML= "from worker:"+e.data;
});



function inlineWorker (msg,fn,onMsg) {
    var 
    w=window,
    U=!!w.webkitURL?w.webkitURL:w.URL,
    src=fn.toString(),
    s=src.indexOf('{'),
    e=src.lastIndexOf('}'),
    worker = new Worker(U.createObjectURL(
        new Blob([ src.substring(s+1,e-1) ], { type: "text/javascript" })
    ));
    if (typeof onMsg==="function") {
        worker.addEventListener("message",onMsg);
    }
    if (msg) {
       worker.postMessage(msg); 
    }
    return worker;
}
<div id="log"></div>
unsynchronized
  • 4,609
  • 2
  • 29
  • 40
-1

This is a bit off topic answer, but ... you may not require the use of web workers to deal with long jobs on browsers.

Let's suppose you want to run several time an heavy computing (like you would do over an array) :

const heavyFct = () => {let i = 0; while(i<1e8) {++i}}

for (let i = 0; i < 100; ++i) {
  heavyFct();
}

This should freeze your browser.

To avoid this we can rely on setTimeout this way:

const desync = (i = 0) => {
  if (i >= 100) {return}
  heavyFct();
  setTimeout(() => desync(i + 1), 0);
}
desync();

Now you have your heavy computing running without freezing you computer

Poyoman
  • 654
  • 7
  • 16
  • I got some down-votes, but without any comments. I'm interested to know why this would be a bad solution ... since i use it ^^ – Poyoman Jan 25 '21 at 15:10