Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to 'thread' Javascript and keep access to the UI?

I would like to thread some Javascript code while both the main process and thread are free to update the browser UI.

For example:

function StartStuff() {
    StartThreadedCode();
    // do more work and update the UI while StartThreadedCode() does its work
}

function StartThreadedCode() {
    // do stuff (do work and update the UI)
}

Is it possible?

like image 698
TERACytE Avatar asked Oct 26 '11 22:10

TERACytE


2 Answers

There are two main ways to achieve "multithreading" in Javascript. The first way is a cross-browser solution, that would also work in older browsers, but is more complicated to implement.

The idea behind it is that you give the UI some time to update every once in awhile. Since there's no synchronous sleep function in Javascript, the only way to achieve this is to use setTimeout (or setInterval with a little bit more complicate logic) to delay the execution of every loop of your complex calculations. This would give the browser some time to update the UI between loops, giving the visual effect of multiple things happening simultaneously. A few ms should be more than enough for the UI to reflect the latest changes.

It has it's drawbacks of course, and can be quite difficult to implement if there are multiple actions the user might want to do while the background calculations are being performed. Also it can drastically slow down the whole background calculation, since it's delayed a few ms now and then. In specific cases, however, it does the trick and performs well.

The second option would be to use web workers, that are basically Javascript scripts running independently in the background, like a thread. It's much easier to implement, you only have to worry about messaging between main code and background workers, so your whole application isn't affected as much. You can read about using them from the link posted by Mic https://developer.mozilla.org/en/Using_web_workers. The greatest drawback of web workers is their support by browsers, which you can see at http://caniuse.com/#search=worker There's no possible workaround for IE <9 or mobile browsers that truly simulate the effect, so there's not much you can do about those browsers, but then again, the benefits of modern browsers might outweigh poor IE support. This, of course, depends on your application.

Edit: Im not sure whether I explained the first concept clearly enough, so I decided to add a small example. The following code is functionally equivalent to:

for (var counter = 0; counter < 10; counter++) {
  console.log(counter);
}

But instead of logging 0-9 in quick succession, it delays 1s before executing the next iteration of the loop.

var counter = 0;

// A single iteration of your calculation function
// log the current value of counter as an example
// then wait before doing the next iteration
function printCounter() {
  console.log(counter);
  counter++;
  if (counter < 10)
    setTimeout(printCounter, 1000);
}

// Start the loop
printCounter();
like image 85
zatatatata Avatar answered Sep 27 '22 22:09

zatatatata


As of 2009 (FF 3.5/Gecko 1.9.1) a new Web API was added that is called Web Workers. It works also on Chrome 4+, Opera 10.6+ and IE10+.

The worker is basically a background thread that runs in a separate process.

The communication between the master process (eg. your UI's main thread) and the slave process (the background thread, the worker) is established with the aid of a generic PostMessage/onmessage function where you can exchange whatever data you like between the two parties.

It is worth mentioning that every single worker is assigned to a different core. For instance by creating 4 different workers (that do a long-running computation) on a quad-processor you are going to see all the 4 CPU-cores like 100% while the main-script is still idling and thus responding to your UI events (look at this example).

A basic example:

main-script.js

if ("function" !== typeof window.Worker) {
  throw "Your browser doesn't support Web Workers";
}
var thread = new Worker("background-thread.js");
thread.onmessage = function(e) {
  console.log("[A] : I received a " + e.data + " :-P");
};
thread.onerror = function(e) {
  console.log(e);
};

console.log("[A] : I'm sending a PING :-)");
thread.postMessage("PING");

background-thread.js

onmessage = function(e) {
  console.log("[B] : I receveid a " + e.data + " :-)");
  console.log("[B] : I will respond with a PONG ;-)");
  postMessage("PONG");
};

The above example should produce the following output at your browser's console:

[A] : I'm sending a PING :-)

[B] : I receveid a PING :-)

[B] : I will respond with a PONG ;-)

[A] : I received a PONG :-P

So happy PING-ing to your background script!

like image 26
Eugen Mihailescu Avatar answered Sep 27 '22 21:09

Eugen Mihailescu