Chrome, HTML5, webdev

Web Workers (Part 3 Out Of 3) – Shared Wrokers

Shared Worker Example

Web Workers - The bookA web worker is a single JavaScript file loaded and executed on a separate thread (=background and not the UI thread). Dedicated web workers are linked to their owner/creator which is the script that called and loaded them. Shared web workers allow any number of scripts to communicate with a single worker.
Btw, if you missed Part1 and/or Part2 of this series, you might want to read them first.
Shared web workers are identified in two ways: either by the URL of the script used to create it or by explicit name. In the code below we will see how it can be done.

Shared web workers can:

  • load further scripts with importScripts()
  • attach error handlers, and
  • run the port.close() method to prevent further communication on a specific port.

Continue reading

Standard
HTML5, webdev

Web Workers (Part 2 Out Of 3) – Control Your Threads In JavaScript

Quick Intro

If you wish to read the first part of this series please go to Web Workers Part 1.
The Web Workers specification defines an API for spawning background scripts in your web application. Web Workers allow you to do things like fire up long-running scripts to handle computationally intensive tasks, but without blocking the UI or other scripts to handle user interactions. They’re going to help put and end to that nasty ‘unresponsive script’ dialog that we’ve all come to love:

or if you are (still) on windows:

 

Unresponsive script dialog

you will get this common unresponsive script dialog.

 

Workers utilize thread-like message passing to achieve parallelism. They’re perfect for keeping your UI refresh, performant, and responsive for users.

Types of Web Workers And Some Code

It’s worth noting that the specification discusses two kinds of Web Workers:

Restrictions with Local Access

Due to Google Chrome’s security restrictions, workers will not run locally (e.g. from file://) in the latest versions of the browser. Instead, they fail silently! To run your app from the file:// scheme, run Chrome with the --allow-file-access-from-files flag set. It is not recommended to run your primary browser with this flag set. It should only be used for testing purposes and not regular browsing.

(!) Debug: The other good thing to know is that you can debug works in the Chrome Developer Tools by clicking on the Scripts tab, and scrolling down in the column on the right to Workers and clicking on the debug checkbox.

Other browsers do not impose the same restriction.

Same Origin Considerations

Worker scripts must be external files with the same scheme as their calling page. Thus, you cannot load a script from a data: URL or javascript: URL, and an https: page cannot start worker scripts that begin with http: URLs.

The Code

 

<!DOCTYPE HTML>
<html>
  <head>
    <title>Web Worker: The highest prime number</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
    <meta charset=utf-8 />
  </head>
  <style>
    #actions {
      position: fixed;
      top: 10px;
      background: lightBlue;
      padding:8px;
    }
    h1 {
      position: relative;
      bottom: 10px;
      left: 280px;
    }
    #status { 
      position: relative;
      font-size: 120%; 
      background: lightyellow;
      margin:8px; 
    }
    article { 
      position: relative;
      color:red; 
    }
    input {
      width: 80px;
      height: 35px;
      font-size: 120%;
    }
  </style>
  <body>
    <script>
      var myWorker;

      function start() {
        console.log("WebWorker: Starting");
        myWorker = new Worker("highPrime2.js");
        myWorker.addEventListener("message", primeHandler, false);
        var maxNum = $('#upto').val();
        myWorker.postMessage({'cmd': 'start', 'upto': maxNum});
      }

      function stop() {
        if (myWorker != undefined) {
          var msg = "<br/>WebWorker: Terminating " + new Date();
          console.log(msg);
          $('#status').append(msg);
          myWorker.terminate();
          myWorker = null;
        }
      }
      function primeHandler(event) {
        console.log ('got e:'+event.data);
        if (is_numeric(event.data)) {
          $('#result').append(event.data);
        }
        else {
          $('#status').append(JSON.stringify(event.data) );
        }
      }

      function is_numeric(input){
        return typeof(input)=='number';
      }
    </script>

    <h1>Web Worker: The highest prime number</h1>
    <article>The prime numbers: 
      <output id="result"></output>
      <div id="status"></div>
    </article>
    <div id="actions">
      <input type="text" name="upto" id='upto'/>
      <button onclick="start()" title="Start the work">Start</button>
      <button onclick="stop()" title="Stop the work and go have a drink">Stop</button>
    </div>
  </body>
</html>

// And here is the worker code:
<script>
  //
  // A simple way to find prime numbers
  //
  self.addEventListener('message', function(e) {
    var data = e.data;
    var shouldRun = true;

    switch (data.cmd) {
      case 'stop':
        postMessage('Worker stopped the prime calculation (Al Gore is happy now) ' + 
          data.msg );
        shouldRun = false;
        self.close(); // Terminates the worker.
        break;
      case 'start':
        postMessage("Worker start working upto: " + data.upto + " (" + new Date()+ ")<br/>");
        var numbers = isPrime(data.upto);
        postMessage("Got back these numbers: "+ numbers + "<br/>");
        break;
      default:
        postMessage('Dude, unknown cmd: ' + data.msg);
    };
  }, false);

  // simple calculation of primes (not the most efficiate - but works)
  function isPrime(number) {
    var numArray = "";
    var this_number,divisor,not_prime;
    var this_number = 3;
    while(this_number < number) {
      var divisor = parseInt( this_number / 2);
      var not_prime = 0;
      while(divisor > 1) {
        if(this_number % divisor == 0) {
          not_prime = 1;
          divisor = 0;
        }
        else {
          divisor = divisor - 1;
        }
      }
      if(not_prime == 0) {
        numArray += (this_number + " ");
      }
      this_number = this_number + 1;
    }
    return numArray;
  }
</script>

 

The last part of this series is about Shared Web Workers.

You can also find full set of example on github: Web Workers Examples.

Standard
HTML5, webdev

Web Workers (Part 1 Out Of 3)

Short History

In modern web applications there are lots of cases when we need to do some stuff in the background. The only way to do it today in most of the modern browsers is by using Web Workers. Web Workers provide a standard way for browsers to run JavaScript in the background.  It let you spawn multiple “threads” that all run at the same time. For more about multithreading this is a good place to start your reading.

Web Workers can do lots of things:

  • Complex mathematical calculations
  • Make network requests
  • Access local storage

all while the main web page responds to the user actions (e.g. scrolling, typing some text or clicking around your app).

What is a Worker?

A ‘worker’ is a script that will be loaded and executed in the background. Web Workers provide a way to do this seamlessly, for example:
new Worker(“worker.js”);
The above will load the script, located at ‘worker.js’, and execute it in the background.

There are some big (very big) limitations (but please don’t worry, we will see how to solve them in the next post):

  • Workers don’t have access to the DOM: No document, getElementById, etc. However, you can use setTimeout, setInterval, and XMLHttpRequest.
  • Workers don’t have direct access to the ‘parent’ page.

Can We Use Web Workers?

In order to find out if we can use web workers we need to check if there is a Worker property on the global window object. If our browser doesn’t support the Web Worker API, the Worker property will be undefined.

isWorkersAvailable() {
  return !!window.Worker;
 }

Instead of writing this function yourself, you can use Modernizr to detect support for web workers (Pss… Modernizr is an open-source JavaScript library that helps you build the next generation of HTML5 and CSS3-powered websites by doing lots of work for you and saving you from reinventing the wheel again and again – Thank you @KuraFire  @paul_irish and @SlexAxton )

if (Modernizr.webworkers) {
  // window.Worker is available!
} else {
  // no native support for web workers
}

 

Short Example

 

//
// A simple way to find prime numbers
//
var n = 1;
search: while (true) {
  n += 1;
  for (var i = 2; i <= Math.sqrt(n); i += 1)
    if (n % i == 0)
     continue search;
  // found a prime!
  postMessage(n);
}

<!DOCTYPE HTML>
<html>
 <head>
  <title>Web Worker: The highest prime number</title>
 </head>
 <body>

  <h1>Web Worker: The highest prime number</h1>
  <article>The highest prime number discovered so far is: 
	  <output id="result"></output>
  </article>
  
   var worker = new Worker('highPrime.js');
   worker.onmessage = function (event) {
     document.getElementById('result').textContent = event.data;
   };
  
 </body>
</html>

 

In the next post I’ll dive deeper on more interesting stuff you can do with workers. We will see how to communicate with one dedicated worker and how we can share workers (just for fun).
Here you can continue reading the second part of this series.

More (good) sources

Standard