Wrapping Functions In Web Workers

How to wrap arbitrary functions, that may take a while, in a web worker thread.

HTML

<button>Do It</button>

CSS

body {
    margin: 1.8em;
}

p {
    font: 0.8em "courier new";
}

JavaScript

/*
 * Wraps "func" with a web worker thread, and
 * returns a jQuery deferred so the caller can
 * get the result.
 *
 */
function worker( func ) {
    return (function() {
        
        // Creates the blob URL. We have to construct
        // the web worker code here by-hand.
        var blob = URL.createObjectURL( new Blob( [ "\
            onmessage = function(){\
                postMessage((" + func.toString() + ")());\
            };"])),
            
        // The web worker the actually runs the code,
        // and the deferred used to pass the result
        // back to the caller.
        worker = new Worker( blob ),
        deferred = new $.Deferred();
        
        // We can resolve the deferred when we hear back
        // from the web worker thread.
        worker.onmessage = function( e ) {
            deferred.resolve( e.data );    
        };
        
        // Start the worker, and return the deferred back
        // to the caller.
        worker.postMessage('');
        return deferred;
    });
}

// Arbitrary worker function that takes
// some time to complete. Returns the time
// it took to do the computations.
function doWork() {
    var start = new Date(), stop;
    for ( var i=0; i<100000; i++ ) {
        for ( var j=0; j<100000; j++ ) {
            j * 1000000;
        }
    }
    stop = new Date();
    return stop - start;
}

// Use the "worker" wrapper to make "doWork",
// an asynchronous web worker thread that returns
// a deferred.
var asyncWork = worker( doWork );

$(function() {

    $( 'button' ).click(function( e ) {
        
        // For illustrative purposes, disable the button.
        $( this ).prop( 'disabled', true );
        
        // Do some work that'll take at least a few seconds
        // to complete.
        asyncWork().done(function( result ) {
            $( '<p/>' ).text( result / 1000 )
                .appendTo( 'body' );
        });
        
        // For illustative purposes, re-enable the button.
...