jQuery Promises, Deferred & WebWorkers
Written by Ian Elliot   
Friday, 21 September 2012
Article Index
jQuery Promises, Deferred & WebWorkers
Web Worker Based Promises

Web Worker Based Promises

The second type of asynchronous programming that occurs in practice is where you use another thread of execution to complete a long running task while the UI thread gets on with what it is supposed to do i.e. look after the UI.

It is assumed that you know how Web Worker works. If not read  JavaScript Web Workers.

There is no doubt that the best way to package a worker thread is as a promise. This isn't difficult but there are are some subtle points and it is easy to become confused.

To wrap a long running function that uses a worker thread as a promise, all we have to do is arrange create a function that returns a promise object. The interesting thing is that the worker code doesn't generally need any changes to make use of a promise. It simply computes the answer or completes the task and then uses the postMessage method to trigger a message event on the UI thread and to return the result.

As example let's use the slow calculation Web Worker introduced in JavaScript Web Workers. First we need a new JavaScript file called worker.js containing the following code:

var total = 0;
for (var i = 0; i <= 10000000; i++) {
    total += i;
};
this.postMessage({ total: total});

You can see that this worker simply loops a lot of times adding integers and when it is done uses the postMessage method to fire a "message" event on the UI thread and supply the result.

The UI thread code is simply:

Button1.addEventListener("click",
  function (e) {
    Button1.disabled = true;
    var worker = Worker("worker.js");
    worker.addEventListener("message",
        function (e) {
          Button1.textContent = e.data.total;
          Button1.disabled = false;
        });
   });

 

Assuming that there is a button on the page to start it all off:

<button id="Button1">Click to start</button>

When the button is clicked the Worker constructor loads worker.js and starts it running on a new thread. The constructor returns a Worker object which runs on the UI thread i.e. the variable worker. 

When the worker thread is finished it fires the "message" event which is handled by the anonymous function. This changes the buttons caption to the total returned by the worker.

This is perfectly simple straightforward use of a Web Worker and so far we have done nothing different. Our next task is to make changes so that the asynchronous aspect of the worker is handled by a Promise object.

All of the new code is in the UI thread.

We need to package the interaction between the UI thread and the worker as a function:

function() mySlowFunc(){

The first thing we need to do is create the Deferred object that eventually will be used to return a Promise object and to change the state of the Promise when everything is finished.

var deferred = $.Deferred();

The next thing we need to do is start the worker off:

var worker = Worker("worker.js");

Now we have a worker thread computing the result over some length of time and what we have to do to make sure that the UI is free to get on with its job is to bring this function to an end and return the promise object. However this doesn't solve the problem of setting the promise object to resolved.

How can we do this so that the UI thread isn't blocked?

The answer is exactly as we have done it before - we set up an event handler for the message event:

worker.addEventListener("message",
   function (e) {
     deferred.resolve( e.data.total)
   });

Notice that we return the value supplied by the worker to the onComplete function.

Now when the worker thread is complete it just calls the onComplete function with the result of the computation. In this case the result is just a simple value but in general it could be an object with lots of values.

Finally we return the promise object.

   return deferred.promise();
}

 

Notice that at this point the worker thread hasn't finished and the promise doesn't have a value as yet. It only gets the value when the worker thread finishes and triggers a message event which calls the event handler.

The complete code for the function is:

function mySlowFunc() {
 var deferred = $.Deferred();
 var worker = Worker("worker.js");
 worker.addEventListener("message",
     function (e) {
        deferred.resolve(e.data.total)
     });
 return deferred.promise();
}

So how do we use the new function and the promise object it returns?

The answer is, as before,  that you use it just like any other asynchronous function that returns a Promise.

Button1.addEventListener("click",
    function (e) {
       Button1.disabled = true;
       mySlowFunc().then(
          function (value){
            Button1.textContent =value;
            Button1.disabled = false;
          });
     });

 

The button's event handler now just has a simple call to mySlowFunc and this immediately returns a promise object  The then method of the promise object is used to define what should happen when the thread completes and returns the "promised" value. That is the function passed to then sets the button caption to the value and re-enables the button.

This is very easy to use and not that much more difficult to setup.

The key idea is that the function that does the calculation runs on the worker thread. The promise and function that returns it run on the UI thread. The UI thread is freed by the function that returns the promise because it sets an interrupt handler that will be called when the worker thread has finished.

Always wrap your long running functions as promises.

Asynchonous jQuery

 

  • Part I   Promises & Deferred - the consumer
  • Part II  Promises, Deferred & WebWorkers  - the producer

 

Related Articles

 

To be informed about new articles on I Programmer, install the I Programmer Toolbar, subscribe to the RSS feed, follow us on, Twitter, Facebook, Google+ or Linkedin,  or sign up for our weekly newsletter.

 

Banner


Javascript data structures - the binary tree

Binary trees in JavaScript? Easy with the right storage mapping function. Find out how to code a binary tree right up to a depth first traversal.



Just JavaScript - Object Construction

Object creation is fundamental to all object-oriented languages, but in JavaScript is is left to the programmer to work out how best to do it and often the practice that you encounter isn't the best b [ ... ]


Other Articles

blog comments powered by Disqus

 



Last Updated ( Tuesday, 12 February 2013 )
 
 

   
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.