Wednesday, August 1, 2012

Mastering in HTML5 Webworkers



One of the intresting useful feature of HTML5 is webworkers. Webworkers are helpful to create responsive and high computational applications. It solves many issues regarding performance of apps and high computations with less resources, we can easily migrate to webworkers to get more performance by implementing apps with webworkers. The blocking of UI and getting the kill pages and Aw,snap massages will no longer exists.


workers

webworker specification api allows the authors to run the scripts in background indepandantly without affecting user interface scripts. webworkers runs like thread. computing in webworkers will give high performance to app.

webworker support checking

Major user agents IE10.0,FF3.5+,chrome 4.0+,Safari6.0+,Opera10.6+ supports webworkers. Programatically checking webworker support.
if(window.Worker)
{
 console.log('webworker support');
}  
else{
 console.log('Oops! no webworker supprt');
}
 

Types of webworkers

We have two kinds of workers. They are
Dedicated workers: it has global access, same for all the documents.
Shared workers: it has ports access, changes with the connection.
*This part covers dedicated workers and the shared workers are covered in next part.


Defining worker

We can define the workers in two ways
external workers
Inline workers/embended workers


External workers

Defining workers needs a url to communicate with the script through transferable objects. This example just demonstrate worker initialization.
var worker= new Worker('worker_script.js'); 
// This defines worker script path to connect

Communication with worker

when postMessage() is called worker handles the data with onmessage() event and it will perform the operations on recieved data. The below code is to send message to worker.
worker.postMessage("Hi this is raju"); 
//this is to send message
  

Handling messages in workers

When message recieved from main page the onmessage event handles data. This example is to handling messages in workers

***** worker_script.js *****

onmessage=function(e){
 //Handle the messages with event
 var data=e.data;
 //this is the data recieved from UI
}

Sending messages from workers

postmessage() is to sending the messages to UI. This code to send message from worker.
onmessage-function(e){
 var data=e.data;
 //recieved data
 postMessage('This is recieved message'+e.data); 
 //sending message to UI
}

Handling worker Messages

worker messages are handle in UI using worker onmessage event. The code just handle messages of workers.
worker.onmessage=function(e){
 var worker_data=e.data; 
 // recieved data from worker
 alert(worker_data);
}

Handling Errors in workers:

worker returns the error lineno, filename, message. The code displays lineno, filename and message.
worker.onerror=function(e){
console.log( 'ERROR: Line '+ e.lineno+ ' in '+ e.filename+ ': '+ e.message);
}
   

Finally a simple example of using worker. This Example exibhits how sending messsage to worker and message from worker.


**** UI script ****

var worker= new Worker('worker_script.js'); 
// This defines worker script path to connect
worker.postMessage("Hi this is raju"); 
//this is to send message
worker.onmessage=function(e){
 var worker_data=e.data; 
 // recieved data from worker
 alert(worker_data);
}
worker.onerror=function(e){
 console.log( 'ERROR: Line '+ e.lineno+ ' in '+ e.filename+ ': '+ e.message);
}
 
  

**** worker_script.js ****

onmessage-function(e){
 var data=e.data; 
 //recieved data from UI
 postMessage('This is recieved message'+e.data); 
 //sending message to UI
}
  

Structered way of message passing to workers/JSON Data handling in workers

Suppose you need to handle the multiple computations in the workers for that you need to handle the data in stuctured format i.e; json format.
look at the example of handling multiple operations in the workers. This example demonstrates how to handle when multiple messages are sent to worker

**** main page ****

var worker=new Worker('worker_script.js');
  
worker.postmessage(JSON.stringify({'op' : 'mult', x : 4, y : 6}));
worker.postmessage(JSON.stringify({'op' : 'add', x : 6, y : 9}));
worker.postmessage('wrong data');
  
worker.onmessage=function(e){
 alert(e.data)
}
 

**** worker_script.js ****

function addNumbers(x,y) {
     return x + y;
}
 
function mulNumbers(x,y) {
   return x*y;
}
  
onmessage = function (event) {
    var data = JSON.parse(event.data);
 
    switch(data.op) {
        case 'mult':
        postMessage(mulNumbers(data.x, data.y));
        break;
        case 'add':
        postMessage(addNumbers(data.x, data.y));
        break;
        default:
        postMessage("Wrong operation specified");
    }
};

Here the code is for sending addition and multiple requests and processing computation in worker then return the result to main page.

workers/ Embended workers:

Inline workers can ba added in two ways one is by using createObjectURL and other is script tag type attribute worker/javascript. This example demonstrate how to add inline workers using blob and createObjectURl.
var bb = new BlobBuilder();
bb.append("onmessage = 
function(e) 
{ 
 postMessage('Inline worker creation'); 
}");

var blobURL = window.URL.createObjectURL(bb.getBlob());

var worker = new Worker(blobURL);
worker.onmessage = function(e) {
  alert(e.data)
};
worker.postMessage();

The above code initializes BlobBuilder and append it with worker code.
Then it create url for blob.
Finally Initializes the worker with generated URL.

Sub workers:

Deviding tasks among workers is the easy using subworkers. Here we are taking multiple operations and passing the objects to the main worker and it will divert the calls to subworkers.

**** main page ****

var worker=new Worker('worker_script.js');
  
worker.postmessage(JSON.stringify({'op' : 'mult', x : 4, y : 6}));
worker.postmessage(JSON.stringify({'op' : 'add', x : 6, y : 9}));
worker.postmessage('wrong data');

worker.onmessage=function(e){
 alert(e.data)
}

**** worker_script.js ****

  
var thisworker=this;
var add = new Worker('subworker1.js');
var mult= new Worker('subworker2.js');
  
add.onmessage=function(e){
 thisworker.postMessage(e.data);
} 

mult.onmessage=function(e){
 thisworker.postMessage(e.data);
} 

thisworker.onmessage = function (event) {
    var data = JSON.parse(event.data);
 
    switch(data.op) {
        case 'mult':
        mult.postMessage(JSON.stringify(data.x, data.y));
        break;
        case 'add':
        add.postMessage(JSON.stringify(data.x, data.y));
        break;
        default:
       thisworker.postMessage("Wrong operation specified");
    }
};

**** subworker1.js ****

onmessage=function(e) {
 var data=JSON.parse(e.data)
    var result= data.x + data.y;
    postMessage(result);
}

**** subworker2.js ****

onmessage = function(e) {
 var data = JSON.parse(e.data)
 var result = data.x * data.y;
 postMessage(result);
}

The above code simple sends the send the multiple messages to the main thread. Then the processing of cumputation done in subworkers.


Terminating the webworkers

Two ways you can terminate workers.
From app : worker.termiante();
From worker: self.close();


Workers Not have access:

The DOM
The window object
The document object
The parent object


Importing scripts in workers

importScripts('Base64.js'); //Single script
importScripts('Base64.js','FileSaver.js'); //multiple scripts importing

*DOM based scripts cannot be import
**After importing total script only worker started
**imported scripts errors should be handled



Worker specific classes

FileReaderSync to read file or blob inside worker
FileWriterSync to write file
RequestFileSystemSync for file system handling
IndexedDBSync in for client side database storage


DOM functions available in workers

atob() //Not in chrome
btoa() //Not in chrome
dump()
clearInterval()
clearTimeout()
setInterval()
setTimeout()
Navigator object
location object
Application Cache

*Some useful APIs are not supported by webworkers like websockets,localstorage,etc..


Classes available in workers

XMLHttpRequest()
Worker()


Webworkers Errors and Debugging:

Chrome developer tools has an advantage of debugging worker scripts. and you can place breakpoints, scope of variables etc... You can also use stacktrace.js

Security locks

Due to chrome security issues and same origin consideration, chrome doesn't allow local file access as worker script, for that you should start the chrome with the flag --allow-file-access-from-files or you need to run the app from your local host.


Things you should keep in mind the objects cannots be passed through webworkers if you try to do this. You will get DATA_CLONE_ERR DOM Exception 25.
Some API's are not supported in workers, if you try to pass or access unsupported API's in webworkers, you will get SECURITY_ERR DOM Exception 18.
If You want pass the objects to the workers,you should use JSON.stringify for object to string and JSON.parse or eval() for String to object.



HTML5 File Transfer using webworkers and XHR2 is demonstrated here. 

Further reading :

  1. webworker detail explanation 
  2. Using webworkers
  3. Webworker standard 
  4. Webworker specificaton 
  5. HTML5 Presentation 
 

1 comment:

  1. I think that in the support checking code it should be window.Worker.

    ReplyDelete