comments

Barbaric Basics: Web Workers

With Barbaric Basics I brush up on basic techniques, practices and APIs. Come along and get them refreshed you too. Who knows? You may even learn something new!

So in something like a couple of weeks I am going to be taking my first ever Microsoft certification exam! I am going to start with the 70-480: Programming HTML5 with JavaScript and CSS3 and get my way through all the web development stack. A certification? - you may ask yourself - Do you even need that? Is it worth anything?

Well I see the following pros to taking the certification:

  1. As a telecommunications engineer turned software developer I always feel like I am missing something, there some knowledge that all computer scientists hold that I never got the chance to study. As such, it is nice to have a curriculum that tells you: look! This is what you need to know to be able to say that you know technology X
  2. I started doing web development in the late age of jQuery and the early age of MV* frameworks. It was almost jumping from MVVM in XAML to MVVM with knockout.js. Because of that I never did get to program directly against the DOM and every time that I saw some explicit DOM manipulation I started to sweat XD. So it is great to finally know how everything works underneath and sweat no more.
  3. Last but not least Active Solution not only pays for the exam, but also gives me a bonus for every certification that I get. Freaking awesome right?

Since I like books, I read the exam reference book which is pretty good but for the fact that it contains some fundamental errors when teaching the basics of JavaScript. In spite of that, it covers a ton of cool web APIs that I knew about but I hadn’t had the chance to look very much into: svg, web sockets, web workers and flexbox, in addition to more basic stuff. To get more comfy with using these APIs and writing Vanilla JavaScript I am going to be writing some blog posts and code samples.

Let’s start with Web Workers. I will be using this sample program: iWrite, a vanilla markdown editor to showcase this API (also available on GitHub).

Web workers iwrite sample

Web Workers

Traditionally, any web applications that run javascript have been doing so in a single-threaded runtime environment. That means that you are limited to the amount of computation that you can do in that single thread if you want to avoid degrading the quality of the experience of your users, and particularly the dreaded unresponsive scripts message.

Web Workers allows you to spawn worker threads to run computation heavy pieces of javascript asynchronously and independently from your web application and the normal javascript runtime lifecycle. In practice, you will instantiate a WebWorker from your website and communicate with it back and forth through messages.

Web workers

To create a new web worker you use the Worker constructor and point it to a separate javascript file:

// iwrite.js
var markdownParser = new Worker('scripts/markdownParser.js')

This will download the file asynchronously and ready the worker to be able to receive messages. The worker will work on its own thread, and in its very own isolated environment which you’ll be able to access through the self keyword. Next, you will add an event handler to receive messages from the web worker:

// iwrite.js
markdownParser.onmessage = handleParsedHtml

Whenever we have some work that needs to be processed by the web worker we will send it a message via postMessage:

// iwrite.js
function computePreview(fileContent) {
  console.log('sending data to worker: ', fileContent)
  markdownParser.postMessage(fileContent)
}

When we send a message to the web worker it will start doing work for us, and when it is ready it will itself send a message back to the main executing thread:

// markdownParser.js
// simplified from actual code
onmessage = function(event) {
  var fileContent = event.data
  parsedHtml = parseMeSomeMarkdown(fileContent)
  postMessage(parsedHtml)
}

That we can handle on the onmessage event handler:

// iwrite.js
function handleParsedHtml(e) {
  var parsedHtml = e.data
  console.log('received data from worker: ', parsedHtml)
  DOM.setPreviewContent(parsedHtml)
}

If you, for any reason, want to destroy your web worker you can do so using the terminate method:

// iwrite.js
markdownParser.terminate()

And the worker itself can do it by calling its own close method:

// markdownParser.js
self.close()

You can also handle errors that occur in your web workers by using an error event handler:

// iwrite.js
markdownParser.onerror = handleParsingError

function handleParsingError(e) {
  var error = e.data
  console.log('OMG there was an error: ', error)
  // error has
  // lineno -> line number
  // filename -> filename that the web worker is running
  // message -> error message
}

Cool right? :)

Some other nifty things that you can do with web workers are:

  • Spawn other web workers
  • Send ajax requests
  • Access the navigator object (geolocation)
  • Import external scripts via importScripts

Web Workers Gotchas

Some things that you need to consider while using web workers are:

  • You have no access to the DOM (window, document) since you are not supposed to do DOM manipulation from a web worker
  • Since there’s no limit in the amount of workers that you can create, you have to be careful and avoid hogging your host computer
  • When sending messages between your web application and working threads you need to be aware that the message itself is copied. So think about how this will impact the performance and memory footprint of your app if you need to send huge messages. Also check this other alternative.
  • If you terminate a worker or a worker closes itself, it is gone for ever, no more sending/handling messages.

Want to Know More About Web Workers?

If you want to learn some more about web workers check out these great in-depth articles: