JavaScript in its current client (browser) implementations does not support multi-threading or any other form of real concurrent programming
1). However, JavaScript in the browser does often portray asynchronous behaviour as:
- some API members (like XMLHttpRequest and parts of Flash and Google Gears) are truly executed parallel to code running in the JavaScript interpreter. However, JavaScript functions called from the API (callbacks) are executed sequentially to any code running in the interpreter.
- use of global functions setTimeout and setInternval allow for a
kind of cooperative multi-tasking .
A web developer therefore can be confronted with several pitfalls of concurrent processes. Trouble is that JavaScript does not provide the programmer with any built-in mechanism to cope with these issues, i.e. you have to build them yourself.
Note that the standard way of dealing with asynchronous behaviour in JavaScript is to plug into its event-driven programming model and utilize
custom events. Although you can directly handle things from within a callback function, it's more convenient to raise an event and let other part of the application worry about handling the event. Note that there are a lots of issues concerning browser compatibility so it is sensible to utilize a JavaScript library to abstract away any differences in event handling between the dominant browser version.
For example: a callback executed by any XMLHttpRequest started from a function "authenticate", will update the global variable "loginStatus" and consecutively raise the event "loginStatusChanged". Several components on the page (a login status indicator, a user profile information box, different content areas which show user-related information) are all subscribed to this particular event and will independently update their contents.
Although different "repaint" functions could be called from within the callback as well, the clear advantage of using events is that it conforms to the idiomatic way of system interaction, while separating the generator of the event (e.g., the XMLHttpRequest callback) from the handler(s) of the event (the page components) means that the separate pieces are kept loosely coupled and can easily be designed and modified independently of each other.
Note that keeping global state in order to handle asynchronous event might not be sufficient to deal with concurrency problems. A typical example would be animations. These are performed having the setTimeout or SetInterval global function repeatedly call a function which will "draw" one "frame" out of a series, creating the illusion of movement. Code below shows an example using JQuery:
var ef = function(e) { //2)
if ($(e.target).css("opacity") > 0.90)
$(e.target).fadeTo(1000, 0);
else
$(e.target).fadeTo(1000, 1);
};
$("div#panel").mouseover(ef); //1)
- A div with id "panel" is assigned a function for the "mouseover"
event
- when the user moves the mouse over the panel, the event-handler
will make the opaque div fully transparent and vice versa
If the event is fired multiple times before the animation has finished, the effect will be ruined as the animation will be started multiple times. Instead you would want the events to wait (or to be ignored) until the animation has finished so the effect would be a smooth on-and off blinking of the div when the user hovers the mouse over the panel.
This can be achieved by creating a so-called
"mutex" variable, which will store the state of whether the function is currently running or not. You could use a global variable for this purpose but this would quickly become unwieldy if more mutex variables would be needed while a fixed number of global variables would prevent utilizing a variable number of functions.
Alternatively implementation could be done in such a way that the mutex is bound to the function object itself. In this case a global factory function "lock" will encapsulate any event handler within an anonymous function which will function as a stub and intercept any call to the event handler.
function lock(f){
var in_f = false; //1)
var _f = function(){
if (in_f) return; //2)
var clear = function(){
in_f = false;
};
var args = [clear]; //3)
for(var i = 0; i < arguments.length; i++){
args.push(arguments[i]); }
in_f = true;
f.apply(this, args); //4)
};
return _f;
}
- the event handler is passed to the lock function. The interface of the event-handler function will change as the lock function will call it with a reference to the clear function
- this function will be called from the callback which is being
executed once the animation has finished.
In this way the animation will be smoothly "blinking". Note that although the function resembles the "synchronized" or "locked" keywords from Java and C# respectively, the JavaScript function is not really equivalent as the mutex is effectively non-blocking: the functions will not wait for the original function to finish. Apart from not having the desired effect in this particular case, the real point to make here is that in JavaScript it would not be possible to implement this is a straight forward way.
A way to achieve this would be to create a synchronization queue to which any halting function would need to be added. After completion of the original event-handler it would then be possible to "continue" with any function reference which was added to the queue.
Note that such a queue is also the way to implement a mechanism when separate asynchronous processes need to be executed sequentially and in a particular order. A good example would be multiple XMLHttpRequest calls. Suppose we model two calls to an synchronous functions with the following code:
function async(timeOut, func){
setTimeout(func,timeOut); //1)
}
}
async(3000, function() {alert("Async1"); next();} );
async(200, function() {alert("Async2"); next();} );
- the setTimeout function will execute the function after the specified timeout in milliseconds.
Note that in this case the second process obviously ends before the first one, somethings which would be a very real possibility when using XMLHttpReuquest. A solution would be the following serialized function queue:
function assyncChain(init){
var _procs = null;
var idx = -1;
var _init = init;
var next = function (){ //3)
idx++;
if (idx >= _procs.length) return; //abort if no more procs to run
var _proc = _procs[idx];
var _args = _proc.arguments || []
_proc.func.apply(null, _args);
};
var add = function (){
var _proc = {};
_proc.func = arguments[0];
_proc.arguments = [];
for(var i = 1; i < arguments.length; i++ ){
_proc.arguments.push(arguments[i]);
} _procs.push(_proc);
};
return function(){ //1)
if (_procs == null) {
_procs = [];
init(add, next);
}
idx = -1;
next(); //2)
};
}
var chain = assyncChain(function init(add, next){ //1)
add(async, 3000, function() {alert("Async1"); next();} ); //4)
add(async, 2000, function() {alert("Async2"); next();} );
});
chain();
- the factory function will return an anonymous function which when called will call the initializer function which need to be passed to it. The user can add the asynchronous functions which need to be added
to the queue from within the initializer.
- after initialization the first function in the queue will be
executed through the next function
- the next function will fetch the next available function from the queue and call it with the arguments as supplied from the initializer function
- the asynchronous function delegates control to the next function
in the queue by calling the "next" function.
Note that the queue can easily be extended with a priority mechanism and with a way to dynamically change the order of execution.
1)Rhino, the "other" JavaScript implementation of the Mozilla Project,
does support multi-threading