Html5 WebWorkers experiment
Table Of Contents
Introduction
So what is this article all about? Well its about a couple of different
things that I have never done before, namely writing my own jQuery
plugin and working with Html5 WebWorker
s.
For those that have never heard of jQuery, it's essentially a JavaScript library. Here is what the jQuery web site says about jQuery
"jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is designed to change the way that you write JavaScript."
http://jQuery.com/ up on date 13/07/2011
Creating your own jQuery plugin is a well documented thing, which you can read about on the jQuery site, but I will be going through this is some detail in the sections below.
Html5 WebWorker
s are essentially threads that are created via
JavaScript, if you are a .NET kind of chap, think
System.Threading.Tasks.Task
. They simple carry out a unit of work in a
dedicated thread. We will see more on this later.
So how are these 2 things used together within this article, clearly I needed some use case. So here was the scenario that I came up with, which is what this article does.
I wanted to create a jQuery
plugin that can be applied to a single element, where the jQuery
plugin would accept an array of search terms. For each of the search terms a new Html5 WebWorker
is spawned that will do a Ajax
flickr search for images that match that search term. When the Html5 WebWorker
completes, the Html5 WebWorker
calls back into the jQuery
plugin, at which point an image wall is created.
In a nutshell that is what this articles code does. We will be going through this in some detail in the sections below.
Authoring A jQuery Plugin
As I stated in the introduction, I wanted to create my own jQuery plugin, as I also stated this is a well documented process which is available at the jQuery website link : http://docs.jquery.com/Plugins/Authoring
My jQuery plugin did not implement all the suggestions at the jQuery website. Here is a list of the features it does implement
- Context
- Maintains Chainability
- Defaults And Options
So see what these mean please consult the jQuery website link : http://docs.jquery.com/Plugins/Authoring
Web Workers The Basic Idea
Html5 WebWorker
s are reasonably new and I guess they are not yet
in widespread usage. So what is the basic idea behind them. Well speaking
plainly a WebWorker is a unit of work that will be carried out on a new thread.
Yes that's right the ability to create new threads in the browser.
Communication Between Host And WebWorker
Communication between the hosting code (the code that creates the WebWorker
)
and the actual WebWorker
is achieved using a PostMessage
like API and a couple
of event handlers that the WebWorker exposes.
Here is what some typical code may look like within the hosting code, notice
how we create a new WebWorker
and hook up its 2 events
onMessage
, is hooked to the workerResultReceiver functiononError
, is hooked to the workerErrorReceiver function
And the worker is started using the PostMessage
API, which is how all message
to/from the worker are done.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// code in WebWorkwer hosting code
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
var worker = new Worker("scripts/FlickrWorkerSearch.js");
worker.onmessage = workerResultReceiver;
worker.onerror = workerErrorReceiver;
worker.postMessage({ 'cmd': 'start', 'msg': settings.searchWords[i] });
function workerResultReceiver(e) {
//so something with the workers data
var result = e.Data;
}
function workerErrorReceiver(e) {
console.log("there was a problem with the WebWorker within " + e);
}
And this is what some typical WebWorker
would look like
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// code in WebWorkwer JavaScript file
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
self.addEventListener('message', function (e) {
var data = e.data;
switch (data.cmd) {
case 'start':
postMessage("The worker says hello");
break;
case 'stop':
self.close(); // Terminates the worker.
break;
default:
self.postMessage('Unknown command: ' + data.msg);
};
}, false);
This simple example simply sends a string back to the host saying "The worker says hello"
Unit Of Work
A unit of work as far as a
WebWorker
is concerned is a single JavaScript file that is passed
to the WebWorker when it is constructed, something like this:
var worker = new Worker("scripts/FlickrWorkerSearch.js");
We will see what a typical worker JavaScript file looks like later within this article.
Importing Other Scripts
You can import other JavaScript files in your main WebWorker by using a line simliar to the one shown below
ImportScripts("scripts/SomeJavaScriptFile.js");
Beware though, the JavaScript fiiles you import must NOT touch the DOM in any way (so that means they probably can't use jQuery internally either, as jQuery uses window (which is in the DOM))
Areas Of Caution
This section outlines some areas where you must be careful when working with
WebWorker
s
No DOM Access
WebWorkers
can not see the DOM, and as such will not be able to
import any script that accesses the DOM, such as jQuery.
Which is a shame as jQuery has
extra non DOM related stuff in it, that would be nice to use in a
WebWorker
, but rules are rules I suppose.
I initially thought I could circumvent this rule by using the DOM in the WebWorker
host and pass in some object into the WebWorker
via a
message, alas this approach failed to, as the object could not be cloned.
The general rule of thumb is that the messages to/from a WebWorker
must be primitive types such as Array/string and other simple types.
No Threading Locking Primitives
OOk so we have the ability to run threads, but unfortunately we have no
threading objects, to ensure the communication of these threads, and ensure that
there is thread safe access to shared data structures. Seems quite a big
oversight. What it does mean is that your WebWorker
and its related
message handler within the host must pretty much be self contained and not
access any shared data structures. I fell into this trap with the code within
this article where I was populating a common Array, until I realised that this
was probably not that smart, and I then moved the Array to be scoped locally to
the WebWorker
and then it was all ok.
How The Demo App Works
These subsections shall outline how the demo app works.
How To Run The Demo
The demo is a VS2010 solution for convience, but do not worry if you do not have VS2010, just open windows explorer and locate the file "Index.html" and right click and open with FireFox, yes that's right I need you to use a specific brower (more on this later).
Browser Support
WebWorker
(s) are supported by the following browsers
Browser | WebWorker supporting option |
---|---|
Chrome | v3 or above |
FireFox | v3.5 or above |
IE | v9 or above |
Opera | 10.6 or above |
Safari | 4 or above |
As can be seen from the table above there is quite good support for WebWorker
(s). Although for this article I am not really concerned with cross browser
compatability and have not spent any time worrying about that at all, yes you can moan about that if you like, but it will not change anything.
My reasons for this are as follows:
- I wanted to write about WebWorker(s) not how to get things to work across browsers
- I simply do not have enough hours free to care about this
- I feel this article demonstrates the key concepts behind using WebWorker(s) just fine, which is what I was going for
As such the only Browser that I know works for sure is the one that I use
most often on the rare occassions I do try and do some web coding, which is
FireFox (v3.5 or above). The reason that some of the other browsers do not work
is that the free Image Gallery jQuery
plugin that I used does not like different size images coming back to it,
FireFox seems to cope with this just fine, but Chrome does not. Go figure. As I
say my focus on this article was the WebWorker
(s) not modifying
someone elses Image Gallery jQuery
plugin, so sorry but it is the way it is.
Html Part
See JavaScript file : Index.html
Most of the html was taken from the freely available Image Gallery jQuery plugin that this article uses. The only thing that I changed was to include my additional JavaScript files, and to create dynamic content for the DIV class="container" element via the use of my own Custom jQuery Plugin which we dicuss next.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Experimenting With HTML5 WebWorkers</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description"
content="Simple Jquery/Html5 WebWorkers demo to build image wall based on WebWorker Flickr search" />
<meta name="keywords" content="jquery, html5, full screen, webworker, flickr" />
<link rel="stylesheet" href="css/style.css" type="text/css" media="screen" />
<script type="text/javascript" src="scripts/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="scripts/jquery.easing.1.3.js"></script>
<script type="text/javascript" src="scripts/FlickrWall.js"></script>
<script type="text/javascript" src="scripts/FullPageImageGallery.js"></script>
</head>
<body>
<div id="webWorkAvailability" style="display :none;" >
<p>WEB WORKERS ARE NOT AVAILABLE</p>
</div>
<div id="fp_gallery" class="fp_gallery">
<img src="images/1.jpg" alt="" class="fp_preview" style="display: none;" />
<div class="fp_overlay"></div>
<div id="fp_loading" class="fp_loading">
</div>
<div id="fp_next" class="fp_next">
</div>
<div id="fp_prev" class="fp_prev">
</div>
<div id="outer_container">
<div id="thumbScroller">
<div class="container">
</div>
</div>
</div>
<div id="fp_thumbtoggle" class="fp_thumbtoggle">View Thumbs</div>
</div>
<div>
</div>
</body>
</html>
Basically this section gets dynamically updated by the use of Custom jQuery Plugin which we dicuss next.
<div class="container">
</div>
As I say most of this html was taken from the freely available Image Gallery jQuery plugin that this article uses
Custom jQuery Plugin Part
See JavaScript file : FlickrWall.js
The jQuery
plugin that I wrote does a pretty simple job. It is applied to a
specific element, where the jQuery
plugin would accept an array of search terms. For each of the search terms a new
WebWorker
is spawned that will do a Ajax
flickr search for images that match that search term. For each WebWorker
spawned my custom jQuery
plugin will hook up to both the onMessage()/onError events from the newly
spawned WebWorke
. When the WebWorker
completes, the WebWorker
calls back into the jQuery
plugin, at which point an image wall is created.
//------------------------------------------------------------------------
//
// This is a simple jQuery plugin that can be applied to a single element,
// where the jQuery plugin would accept an array of search terms.
// For each of the search terms a new Html5 WebWorker is spawned that will
// do a Ajax flickr search for images that match that search term.
// When the Html5 WebWorker completes, the Html5 WebWorker calls back into the
// jQuery plugin, at which point an image wall is created.
//
//------------------------------------------------------------------------
(function ($) {
$.fn.FlickrImageWall = function (options) {
var wwsAreOk = false;
var workersCompleted = 0;
var src = "";
var workerArray = new Array();
var imagesSoFar = 0;
var maxImages = 15;
//Check for WebWorker availability
if (Supports_web_workers()) {
$(".webWorkAvailability").hide();
wwsAreOk = true;
}
//Assume these setting values, unless new values are
//supplied by the caller of this plugin
var settings = {
'searchWords': ['dog', 'cat', 'shark']
};
//The is the call back from the WebWorker that is called
//when the worker sends a message back to this hosting jQuery plugin
//via the PostMessage API
function workerResultReceiver(e) {
//Each worker must have its only local data, cant modified unsafe global fields, as they are not thread safe.
var workerImages = new Array();
var jsonData = $.parseJSON(e.data);
var src;
for (var i = 0; i < 5; i++) {
src = "http://farm" + jsonData.photos.photo[i].farm + ".static.flickr.com/" + jsonData.photos.photo[i].server +
"/" + jsonData.photos.photo[i].id + "_" + jsonData.photos.photo[i].secret + "_b.jpg";
workerImages.push(src);
}
PopulateWall(workerImages);
//check to see if all the web workers have completed yet, and if stop all
//workers by sending a new stop message
//Pretty sure the access to imagesSoFar is not thread safe, but had no choice
imagesSoFar = imagesSoFar + 1;
if (imagesSoFar == workerArray.length) {
for (var j = 0; j < workerArray.length; j++) {
workerArray[j].postMessage({ 'cmd': 'stop', 'msg': null });
}
}
}
//The is the call back from the WebWorker that is called
//when the worker sends a error message back
function workerErrorReceiver(e) {
console.log("there was a problem with the WebWorker " + e);
}
//The jQuery meat, this is what will be run against the selected
//element set that this jQuery plugin is applied to
return this.each(function () {
if (options) {
$.extend(settings, options);
}
//allows chaining of jQuery plugins
var $this = $(this);
//if webworkers are supported
if (wwsAreOk) {
//for each keyword, need to start a new web worker off that will search Flickr
for (i = 0; i < settings.searchWords.length; i++) {
var worker = new Worker("scripts/FlickrWorkerSearch.js");
worker.onmessage = workerResultReceiver;
worker.onerror = workerErrorReceiver;
worker.postMessage({ 'cmd': 'start', 'msg': settings.searchWords[i] });
workerArray.push(worker);
}
}
});
//populate the wall by building up dynamic content based on the Ajax fetch Flickr
//data. Finally make a call to the ImageGallery jQuery plugin, via the CreateWall() function
function PopulateWall(images) {
var fullcontent = "";
for (var j = 0; j < 5; j++) {
var imagName = images[j];
var fullstring = "<div class=\"content\"><div><a href=\"#\"><img src=\"" +
imagName + "\" alt=\"" + imagName + "\" class=\"thumb\" /></a></div></div>";
fullcontent = fullcontent + fullstring;
}
$(".container").append(fullcontent);
CreateWall();
}
//checks for WebWorker support
function Supports_web_workers() {
return !!window.Worker;
}
};
})(jQuery);
$(document).ready(function () {
//hook up FlickrImageWall jQuery plugin to the single element that will receive the dynamic images being
//added to it
$(".container").FlickrImageWall({ 'searchWords': ['bikini', 'tatoo','water']
});
});
I should point out that the call to CreateWall()
calls into a 3rd party jQuery
plugin which is discussed within this article at Image Gallery Part
The WebWorker Part
See JavaScript file : FlickrWorkerSearch.js
As you now know WebWorker
(s) must be created using a standard
JavaScript. You now also know that a WebWorker
can not touch the
DOM. And I have already stated that my problem domain dictated that each WebWorker
would do an Ajax request against the Flickr API. How is this done. Well we just
used a manual Ajax call.
Here is the full code for the WebWorker
Javascript file
"FlickrWorkerSearch.js" which is used within my custom
Query
plugin "FlickrWall.js" when creating the new WebWorker
(s)
to do the searching.
//Does the Ajax flickr search based on a given url and then
//either posts the Ajax response or null using the PostMessage API
function GetData(url) {
try {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
postMessage(xhr.responseText);
}
}
};
xhr.send(null);
} catch (e) {
postMessage(null);
}
}
//Adds a listener to the message event
//This is the main message pump for the PostMessage API for the WebWorker,
//this deals with all the different message types that the worker/host can use
//to communicate
self.addEventListener('message', function (e) {
var data = e.data;
switch (data.cmd) {
case 'start':
var url = "http://api.flickr.com/services/rest/?method=flickr.photos.search" +
"&api_key=FLICKR_API_KEY&tags=" + data.msg +
"&safe_search=1&per_page=20&format=json&nojsoncallback=1";
GetData(url);
break;
case 'stop':
self.close(); // Terminates the worker.
break;
default:
self.postMessage('Unknown command: ' + data.msg);
};
}, false);
Please Note :
I have been good enough to leave in my Flickr API key to give you a fully working demo, but please do not copy it or change the code to slam Flickr with requests, or generally do anything that would cause Flickr to cancel by developer key. Basically please play nice.
Image Gallery Part
See JavaScript file : FullPageImageGallery.js and jquery.easing.1.3.js
This is perhaps the prettiest part of my demo, and alas I can take no credit for this, this is simply one of many free jQuery image library plugins which are available on the internet.
The original source is freely available : http://www.ajaxshake.com/plugin/EN/781/0a2daf19/jQuery-full-page-image-gallery-full-gallery.html where this jQuery plugin (see "FullPageImageGallery.js") also makes use of a jQuery easing plugin, which is the "jquery.easing.1.3.js" file
I did have certain timing issues, due to WebWorker
(s)
essentially being a new thread of execution, which the designer of the image
gallery obviously did not think about. This led me to refactor the image library
code to allow it to be called on reciept of a WebWorker
posted
message inside my own jQuery plugin. These changes were small and
for the large part are not relevant to this article. I did however have to come
up with a way to dynamnically manipulate the DOM for the window element that was
the target for the image library jQuery plugin. That is not really
that relevant to the scope of this article though, rest assured I had to do some
DOM manipulation in order to get a dynamic wall using this jQuery
plugin
I have to say though that the reason I picked this particular image library jQuery plugin is that its really really cool.
But Wait Browser Weirdness
There is however one issue with this free jQuery
plugin, in that it kind of assumes all the images will be of a certain size and
that they shall be square (which is obviously something I can not garuentee as
the images are coming from Flickr). FireFox seems to cope with this just fine
but other browsers (such as Chrome) do not. As the main focus on this article
was the WebWorker
(s) not modifying someone elses Image Gallery jQuery
plugin, so sorry but it is the way it is.
Anyway apart from the Browser issues, once it is populated with images that come back from the WebWorker
(s)
Ajax requests, it will use the dynamically created content as shown below, where
the user may scroll left to right using the mouse, and then click on an image
(click the image below for bigger image)
Once an image is clicked on, it will expand to full screen at which point the user may scroll through full screen images using the left/right buttons, or return to the thumbnails again. This is shown below (click the image below for bigger image)
All in all I was very happy with this free jQuery plugin.
To be honest I think Microsoft could learn a lot from the jQuery
/community
based contributions. Imagine if people could just extend WPF/Silverlight the way
you could jQuery,
and put that stuff out there. I'll give you an
example this image library using a animation jQuery library. So
lets look at that some more, I wrote a jQuery plugin that called a
freely available jQuery image library plugin which in turn depends
on yet another freely available jQuery animation plugin.
If we were in Microsoft land (as I often am), we would have to search the internet searching for Silveerlight/WPF controls on codeplex or whereever and we would probably find something that 10-50 people had downloaded, or we would simply have to wait to see what Microsoft had install for us with .NET(x).
In contrast, when you search for
something like an image library for jQuery
, there are literally
100nds to choose from. It is just so much more community based, as it's not so
closed to extension, that much is obvious.
However with anything that open, you will end up with things that look good but end up being untested, rough and plain unsuable, however if you are wiklling to take a punt and a while on the internet there are some gems out there.
Me personally I like the internet, so am willing to hunt out the gems.
Conclusion
That's it for now, I know I am not known for my web based community contributions, and to be honest I do not see that changing, but it is always worth while making sure you know how to do things in popular technologies. I do see that HTML5 will become very popular, but in my opinion there are some really dumb things in it that need to be fixed such as the things I have found within this article.
You know such as there now being support for extra threading (yes via WebWorker(s)
), but having no native
JavaScript mechanism for doing thread safe stuff is pretty weird, you know things
like lock(..)
, and all of the other locking primitives we have available inside .NET/Java
or most staticly types languages, heck even most langauges for that matter.
I do get the idea of there not being any access
allowed to the DOM from the WebWorker
, well I kind of do. It would have been better to allow it, but it has to be with thread affinity,
where some sort of thread affinity object is available to the WebWorker
. Windows has
used this model like forever, Java
is suprising tolerant of thread
affinity, I don't care
which approach is taken, but the blanket rule of "NO DOM"
JavaScript interaction in a WebWorker
is pretty restrictive, you know jQuery
has lots of good stuff in it, I know it's primarily a DOM manipulation API, but
there is loads of other very useful stuff on offer, such as the ability to do
Ajax
calls, parse JSON
all sorts of stuff really.
Anyway enough ranting, hope you enjoyed the article
Post Comment
I truly appreciate this article post.Really looking forward to read more. Awesome.
My brother suggested I might like this web site. He was entirely right. This post actually made my day. You can not imagine just how much time I had spent for this info! Thanks!
several months back. аАТаЂаFor our business it as an incredibly difficult time,аАТаЂа he was quoted saying.
Im thankful for the blog.Really looking forward to read more. Much obliged.
I value the article post.Really thank you! Keep writing.
Very nice article. I definitely love this site. Keep it up!
You completed a number of fine points there. I did a search on the issue and found most persons will consent with your blog.
Very good article.Really looking forward to read more. Really Great.
Just Browsing While I was surfing yesterday I saw a excellent post concerning
You finished approximately nice points there. I did a explore taking place the deliver and found mainly guys will commend with your blog.
usually posts some pretty exciting stuff like this. If you
This is a really good site post, im delighted I came across it. Ill be back down the track to check out other posts that
Say, you got a nice blog post.Really thank you! Keep writing.
This is my first time visit at here and i am genuinely impressed to read all at one place.
Major thanks for the article.Thanks Again. Awesome.
You produced some decent points there. I looked on the internet for just about any issue and discovered most of the people may perhaps go in conjunction with with your web page.
magnificent post, very informative. I wonder why the other specialists of this sector do not notice this. You must continue your writing. I am sure, you have a great readers a base already!
There is noticeably a bundle to know about this. I assume you made sure good factors in options also.
Well I really enjoyed reading it. This article provided by you is very effective for correct planning.
I simply could not depart your site before suggesting that I really enjoyed the usual information a person provide to your visitors? Is gonna be back frequently to check out new posts
You made some respectable factors there. I seemed on the web for the difficulty and located most people will go together with together with your website.
post. They are very convincing and can certainly work. Nonetheless, the posts are very brief for starters.
Thank you ever so for you blog article.Really looking forward to read more. Really Great.
Very interesting details you have mentioned , appreciate it for putting up. Death is Nature as expert advice to get plenty of Life. by Johann Wolfgang von Goethe.
Keep up the superb piece of work, I read few posts on this website and I believe that your site is really interesting and has got circles of great info.
Very informative blog article.Really looking forward to read more. Cool.
I truly appreciate this blog.Really thank you! Cool.
There as definately a lot to learn about this topic. I love all of the points you have made.
Major thankies for the blog article. Keep writing.
Pretty! This was an incredibly wonderful post. Thank you for supplying these details.
This genuinely answered my challenge, thank you!
Thanks again for the blog.Really thank you!
This blog is obviously interesting and informative. I have picked many useful things out of this blog. I ad love to go back every once in a while. Thanks a lot!
Simply wanna remark that you have a very decent web site , I enjoy the design and style it actually stands out.
Spot on with this write-up, I seriously believe that this site needs a lot more attention. I all probably be returning to read through more, thanks for the info!
Videos lesbiennes asiatiques beurettes sex video
that should outweigh Owens touchdowns. I think all of it
If you don at mind, where do you host your weblog? I am looking for a very good web host and your webpage seams to be extremely fast and up most the time
Its like you read my mind! You seem to know a lot about this, like you wrote the book in it or something.
That as truly a pleasant movie described in this paragraph regarding how to write a piece of writing, so i got clear idea from here.
Thanks for the blog post.Thanks Again. Really Cool.
Thank you for your blog.Really thank you! Keep writing.
Way cool! Some extremely valid points! I appreciate you penning this article and the rest of the site is also really good.
Very good article! We are linking to this great content on our site. Keep up the good writing.
Its hard to find good help I am regularly proclaiming that its difficult to find good help, but here is
Pretty nice post. I just stumbled upon your blog and wished to say that I ave truly enjoyed browsing your blog posts. In any case I all be subscribing to your feed and I hope you write again soon!
Usually I do not learn post on blogs, however I wish to say that this write-up very forced me to take a look at and do so! Your writing style has been surprised me. Thanks, very great article.
some really great content on this site, regards for contribution.
Its hard to find good help I am forever proclaiming that its hard to get good help, but here is
Nathan It as really a nice and helpful piece of information. I am satisfied that you just shared this useful information with us. Please keep us up to date like this. Thanks for sharing.