This post is a kind of experiment. It’s not a strict translation of my previous post written in polish. I’m just curious how “uncle Google” will behave and if he send me some visitors
Notice that english is my second language, so you will have to bear some my language mistakes. In case this project succeedes, I will write some more english stuff.
I’m going to write about one of HTML5 new features — Web Workers. These are intended to provide some heavy computations in the background and — what is most important — web browser remains responsive. It means that you won’t experience any sloweness. When Web Worker ends its job, it notices browser about the outcome. The point is to use user’s CPU which is rarely full used in opposite to server’s.
By the way, multithreading in browser is not a big deal, Facebook uses it to provide you instant messaging or wall refreshing without reloading the whole page.
Official draft of Web Workers API informs that workers have many restrictions, such as no access to DOM or (in our case) jQuery object. You may think of it as a closed environment. However you are able to communicate in both ways via JSON and postMessage() function.
Take a look at our goal. We can control spawning of new Workers, it state update intervals and the moment of termination. To ensure that our browser supports Web Workers, we’ll use nice library specialized in checking a lot of modern HTML features — Modernizr. In both cases user reads alert message if he can use workers or not.
Basicly we need two files: worker and main html file. Below is JavaScript code of timeworker.js:
var workerNo;
onmessage = function(e) {
workerNo = e.data.workerNo;
changeInterval(e.data.timer);
}
function changeInterval(interval) {
setInterval("gimmeTime()",interval);
}
function gimmeTime() {
postMessage({
time:new Date().toUTCString(),
workerNo:workerNo
});
}
- Field workerNo contains worker id — integer 1 or greater.
- onmessage is message recieved from the browser. Notice that we can handle recieved data via e.data, not e alone!
- changeInterval is function which self calls gimmeTime() function with given parameter interval (in microsecs).
- gimmeTime sends browser JSON message with its id and current time via postMessage() function.
Now it’s time for workersdemoeng.html (I’m listing only JS code):
var workers = new Array();
var counter = 0;
var bindedWorkers = '';
if (Modernizr.webworkers) {
alert("Your browser supports Web Workers technology, you're free to test it!");
}
else {
alert('Your browser does NOT support Web Workers technology. Install yourself a decent browser (FF, Chrome, Opera[?])!');
}
function addNotification(workerno) {
var msg = $('<p>').appendTo('#notif');
msg.text('Worker '+workerno+' updated state');
msg.fadeOut(1500);
}
function fireUpWorker() {
++counter;
var workerDiv = $('<div>').attr('id','workerdiv-'+counter).appendTo('#counters');
var par = $('<p>').attr('id','worker-'+counter).appendTo(workerDiv);
$('<input />').attr({
type:'submit',
value:'terminate',
onClick:'terminateWorker('+counter+');return false;'
}).appendTo(workerDiv);
workers[counter] = new Worker('timeworker.js');
var timer = $('#intrv').val();
workers[counter].postMessage({timer:timer,workerNo:counter});
bindWorkers();
}
function terminateWorker(workerNo) {
workers[workerNo].terminate();
$('#worker-'+workerNo).text('terminated!');
$('#workerdiv-'+workerNo).fadeOut(5000);
}
function bindWorkers() {
bindedWorkers += "workers["+counter+"].onmessage = function(event) {";
bindedWorkers += "$('#worker-"+counter+"').text('Worker '+event.data.workerNo+' date : '+event.data.time);";
bindedWorkers += "addNotification(event.data.workerNo);";
bindedWorkers += "};";
eval(bindedWorkers);
}
- We have 3 global fields: array workers which holds all active workers, workers counter and bindedWorkers which contains ugly JS code to be executed by eval() — the only way I know to force browser to listen all workers, not only the last one created.
- addNotification shows short 1,5 sec. notification about worker state update.
- fireUpWorker is the main function. Firstly it creates a new div, and then paragraph and terminate button. Newly created worker receives JSON message about its number and state update interval. At the end, bindWorkers() is executed.
- terminateWorker firstly kills certain worker by worker.terminate(), and then hides div related to it.
- Finally the function I’m least satisfied of: bindWorkers(). It handles what browser should do with the received message from a worker. It works analogically like the worker’s one: through onmessage and e.data. To bindedWorkers string we’re appending the code and executing by eval(). The worst part is when we terminate some workers, this string contains useless data (we could cut this out by some regexps, but I’m lazy
).
I have a big request for the JS geeks: how to run onmessage code in a loop and without eval?
P.S.: It seems that I have found a bug in Chrome browser: an attempt to add onclick attribute by jQuery didn’t worked. You should use onClick instead
