Multi-threading in JavaScript

Notice: This is a discussion thread for comments about the SitePoint article, Multi-threading in JavaScript.


Really interesting article. I wonder if a full blown task scheduler could be implemented. :slight_smile:

Thanks for the article, James. This really made me think.
I like how you always push the boundaries of Javascript, and are willing to share them.

I used this technique for QuiteBASIC. It is a Javascript interpreter for classic BASIC. In my first prototype, I broke up processing such that each program line (BASIC line) was processed separately. But it turns out that some browsers introduce a lot of overhead per timer invocation. So I ended up processing a number of lines at a time.

I guess the lesson is that the chunks of processing should not be so big that they introduce UX unresponsiveness, but also not so small that the scheduling overhead becomes significant.

This article could be interesting, but the usage of setInterval, instead of setTimeout, could makes things even worst.
My humble suggestion is to read a couple of articles, as example from John Resig, about timings, and rethink some step.
Best Regards

yes that is a huge ungainly switch statement. You’re repeating yourself an awful lot there. Wouldn’t something like this be a bit more clever?

function compute()
{
var move = null;

var busy = false, task = ‘init’;
var processor = setInterval(function()
{
if(!busy)
{
var tasktransitions = {
init: [‘tactic1’,‘doit’,‘tactic2’],
tactic2:[‘tactic2’,‘doit’,‘tactic3’],
tactic3:[‘tactic3’,‘doit’,‘pass’],
doit:[‘doit’,‘final’],
pass:[‘pass’,‘final’],
final[clearInterval]
}

 var arg = task==final?processor:undefined, par=tasktransitions[task];    
if(par) {
move = window[par[0]](arg);
if(move) { task = par[1] };
else if(par[2]) {
    task = par[2];
  }
}
busy = false;

}

}, 100);
}

Care to explain what your code is doing breton?

Oh sorry. I noticed that the switch statement’s cases has a lot of code in common.

here’s one of them

   case 'init' :  
   
     move = tactic1();
     if(move) { task = 'doit'; }
     else { task = 'tactic2'; }
   
     busy = false;
     break;

essentially what each case is doing is this:

   case [taskname]:  
   
     move = [var1]();
     if(move) { task = [var2]; }
     else { task = [var3]; }
   
     busy = false;
     break;

so I made an object whose keys are tasks, (the thing the switch is switching on), and whose value is an array containing the values of [var1], [var2], and [var3].

So I use the task to look up those variables, and I plug them into one common chunk of code. (this is unclear because it seems my function got corrupted when it was posted to the blog for some reason).

and though it was not strictly speaking necessary, I added this
var arg = task==final?processor:undefined, par=tasktransitions;

To deal with a special case. (“final” is a bit different from the others).

So sorry, I just realized that this comment form will mangle my last comment as well. Here it is in pastebin form.

pastebin.com/m8aad037

That’s a cool idea James. It reminds me of this Actionscript multi-threading hack I came across when I was trying to figure out how to do pre-caching in Flash without affecting the framerate of an animation:

blogs.adobe.com/aharui/2008/01/threads_in_actionscript_3.html

Sometimes instead of setInterval, I use setTimeout, like this:


function task1() {
    // something here..
    setTimeout (task2, 1);
}
function task2() {
    // something here..
    setTimeout (task3, 1);
}
function task3() {
    // something here
}

And for some for loops or while loops, I created a simple script…


function myfor(obj) {
	var time = 0;
	function next() {
		if (!obj.condition()) return;
		time ++;
		obj.body ();
		obj.increment ();
		setTimeout (next, 1);
	}
	obj.start ();
	next ();
}


var i;
var myloop = {
    start: function() { i = 0; },
    condition: function() { return i < 10000; },
    increment: function() { i ++; },
    body: function() {
    	document.title = i;
    }
};
myfor (myloop);

This would work like a normal for loops, but instead of running everything all the thing once, it will run the loop body, wait for 1 millisecond to allow user to interact with the browser, and then run the body until the condition returns false.

But some times using this is much slower because setTimeout was, well, sometimes be slower than just for loops.

So… I made some changes to the next() function:


	function next() {
		if (!obj.condition()) return;
		time ++;
		obj.body ();
		obj.increment ();[COLOR="Blue"]
		if (time % 10 == 0)[/COLOR]
			setTimeout (next, 1);[COLOR="Blue"]
		else
			next ();[/COLOR]
	}

From what you see, the loop body will run at most 10 times at once, and then wait for a while, and then run the body 10 more times…

I hope that this post will be useful! :slight_smile:

I too, would be interested in any comparisons with the efficiency of using setTimeOut(0) in a similar manner (which I have been known to do for large tasks.)

I’m sure I read somewhere recently that the actual interval is unreliable in some browsers - does that affect the outcome?

“This would work like a normal for loops, but instead of running everything all the thing once, it will run the loop body, wait for 1 millisecond to allow user to interact with the browser, and then run the body until the condition returns false.”

The problem with this of course is that the minimum interval most browsers will give you is 10 milliseconds: a price the browser pays for running on top of an operating system.

Google Chrome supposedly gets around this limitation though, I have no idea how.

Per the difference between setTimeout and setInterval, as I understand it, they both generate events, which get put on a queue. Since a webpage basically runs in a single thread, only one event can be handled at a time, so the events on the queue just run in order, one after another- including the timers. Thus, the timer never executes EXACTLY in the interval you specify. The timer only generates an event exactly when you specify, and the event function itself only gets executed whenever the browser’s single thread gets up to it in the queue. One additional consequence is that if a setInterval event happens, and there’s already a setInterval event on the queue that hasn’t been executed yet, the new event doesn’t get added to the queue. So setInterval events get dropped. The smaller you set your interval, and the more complex your function, the more likely they are to get dropped.

this doesn’t happen with setTimeout however. Since the new setTimeout events don’t get added until the last setTimeout function has finished, you never have more than one timer event on the queue at a time.

hey, where’s the game? i want to play. :slight_smile:

Brilliant! Yes, I have to do recursion too. I have a hierarchy of 7000 nodes and need to walk the tree in a loop. This solution makes it possible without freezing the browser. I did something similar for non-recursive loop on a canvas graph (9000+ points to graph) using setTimeout but I was having a brain-freeze on how to handle it with the recursive function call.