How to run javascript function in "background" / without freezing UI

Apparently there is no real way to run something on background...

There is on most modern browsers (but not IE9 and earlier): Web Workers.

But I think you're trying to solve the problem at the wrong level: 1. It should be possible to loop through all of your controls in a lot less than five seconds, and 2. It shouldn't be necessary to loop through all controls when only one of them has changed.

I suggest looking to those problems before trying to offload that processing to the background.

For instance, you could have an object that contains the current value of each item, and then have the UI for each item update that object when the value changes. Then you'd have all the values in that object, without having to loop through all the controls again.


You can use web workers. Some of the older answers here say that they're not widely supported (which I guess they weren't when those answers were written), but today they're supported by all major browsers.

To run a web worker, you need to create an instance of the built-in Worker class. The constructor takes one argument which is the URI of the javascript file containing the code you want to run in the background. For example:

let worker = new Worker("/path/to/script.js");

Web workers are subject to the same origin policy so if you pass a path like this the target script must be on the same domain as the page calling it.

If you don't want to create an new Javascript file just for this, you can also use a data URI:

let worker = new Worker(
    `data:text/javascript,
    //Enter Javascript code here
    `
);

Because of the same origin policy, you can't send an AJAX request from a data URI, so if you need to send an AJAX request in the web worker, you must use a separate Javascript file.

The code that you specify (either in a separate file or in a data URI) will be run as soon as you call the Worker constructor.

Unfortunately, web workers don't have access to neither outside Javascript variables, functions or classes, nor the DOM, but you can get around this by using the postMessage method and the onmessage event. In the outside code, these are members of the worker object (worker in the example above), and inside the worker, these are members of the global context (so they can be called either by using this or just like that with nothing in front).

postMessage and onmessage work both ways, so when worker.postMessage is called in the outside code, onmessage is fired in the worker, and when postMessage is called in the worker, worker.onmessage is fired in the outside code.

postMessage takes one argument, which is the variable you want to pass (but you can pass several variables by passing an array). Unfortunately, functions and DOM elements can't be passed, and when you try to pass an object, only its attributes will be passed, not its methods.

onmessage takes one argument, which is a MessageEvent object. The MessageEvent object has a data attribute, which contains the data sent using the first argument of postMessage.

Here is an example using web workers. In this example, we have a function, functionThatTakesLongTime, which takes one argument and returns a value depending on that argument, and we want to use web workers in order to find functionThatTakesLongTime(foo) without freezing the UI, where foo is some variable in the outside code.

let worker = new Worker(
    `data:text/javascript,
    function functionThatTakesLongTime(someArgument){
        //There are obviously faster ways to do this, I made this function slow on purpose just for the example.
        for(let i = 0; i < 1000000000; i++){
            someArgument++;
        }
        return someArgument;
    }
    onmessage = function(event){    //This will be called when worker.postMessage is called in the outside code.
        let foo = event.data;    //Get the argument that was passed from the outside code, in this case foo.
        let result = functionThatTakesLongTime(foo);    //Find the result. This will take long time but it doesn't matter since it's called in the worker.
        postMessage(result);    //Send the result to the outside code.
    };
    `
);

worker.onmessage = function(event){    //Get the result from the worker. This code will be called when postMessage is called in the worker.
    alert("The result is " + event.data);
}

worker.postMessage(foo);    //Send foo to the worker (here foo is just some variable that was defined somewhere previously).

Tags:

Javascript