workers

👷‍♀️ Web Workers workshop. 👷‍♂️ Service Workers examples.


Project maintained by yvesgurcan Hosted on GitHub Pages — Theme by mattgraham

Web Worker Workshop

Some use cases

Part A: Set up your environment

Get your macOS ready.

Step 1: Visual Studio Code

Download and install Visual Studio Code (75 MB for v1.39).

Step 2: Homebrew

Install Homebrew.

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Step 3: NPM

Install Node with Homebrew. NPM is included with Node.

brew install node

Check installed version of Node and NPM.

node -v
npm -v

If NPM is not the latest version, you can update it itself.

npm i -g npm

Step 4: Git

Install Git with Homebrew.

brew install git

Check installed version of Git.

git --version

Step 5: GitHub

Create a GitHub account.

Part B: Set up the repository

Let’s make your own project.

Step 1: Fork this repository on GitHub

Click the “Fork” button on the repository page.

Step 2: Clone the repository

Use Git to copy the repository locally.

git clone URL_OF_FORKED_REPO

Step 3: Install and start development server

Enter the directory and run NPM commands.

cd PATH_TO_LOCAL_REPO/workshop
npm i
npm start

Now browse to http://localhost:8080. You should see this message:

It works!

In the workshop/ directory, you will find a couple of files:

Part C: A simple web worker

Alright let’s write some code! The goal here is to create a very simple form. When you submit the form, the main thread will send a message to the worker. The worker will receive and respond by sending a message back to the main thread.

Event model for web workers

Step 1: Form

You probably want to open your browser developer console for the rest of this workshop.

In index.html, let’s create a form with an ID and add a button and input to it. We’ll use this form to send messages to our web worker.

Step 2: Instantiate the worker

In index.js, instantiate your worker thanks to the Worker object.

Example:

const worker = new Worker('public/worker.js');

If you did it right, you’ll see a message from the worker thread in your console 🙂

Step 3: Send messages to the worker thread

In index.js, create a sendMessage function. Define this function as an onsubmit handler for the form.

The function takes event as an argument (the data from the input field lives somewhere in event.target, find it!) and uses postMessage to send the data to the worker.

Hint: You should probably use preventDefault in sendMessage() to prevent the page from reloading when you submit the form.

Step 4: Receive messages from the main thread

In worker.js, add an onmessage handler. Similarly to sendMessage in the main thread, it also takes event as an argument.

Can you console.log the payload sent from the main thread?

Step 5: Receive messages from the worker thread

The worker now receives payloads. Let’s set up the main thread to receive messages from the worker.

Back in index.js, create a new receiveMessage function and associate it with the worker’s instance onmessage handler.

Hint: Before knowing if this handler really works, you’ll have to do the next step.

Step 6: Send messages to the main thread

In worker.js, use the postMessage method of the worker script in the onmessage handler you wrote (I know, it’s getting confusing!) to send a payload back to your app.

Hooray! 🎉 Now, your main thread and work thread are talking to each other. Your main thread can send a payload to the worker, then the worker can process it, and send the result back!

Part D: Keep your UI hot

Let’s take it to the next level. We’re going to generate images first from the main thread and then from the worker thread and then compare performances.

Step 1: Stopwatch

Let’s implement a simple “stopwatch” in index.html and index.js that starts as soon as the page loads and increments every second. For example, add a div with an ID in index.html. Grab that div in index.js and use setInterval and innerHtml to update your page with the new value.

Step 2: Another input

In index.html, add an input tag to your form. If you want to get fancy, you can also label the two fields with Width and Height.

Step 3: Add an image

In index.html, add an img tag with an ID to your page. Don’t set the src attribute yet!

Step 4: Generate an image from the main thread

Ok. This is where things get even more interesting!

Let’s import the createImage.js script into index.js like so:

import createImage from './createImage';

We’re going to duplicate the sendMessage function to create a new handleImageCreation function. Here is the code of that function:

async function handleImageCreation(event) {
    event.preventDefault();
    const width = event.target[0].value;
    const height = event.target[0].value;
    const blob = await createImage(width, height);
    if (blob) {
        const imageData = URL.createObjectURL(blob);
        image.src = imageData;
    }
}

Make sure to grab your img element with getElementById and define it as image in index.js for the function to work.

Then, change the onsubmit handler of the form element to use handleImageCreation.

Ready for fireworks? In your browser, enter a width and height and submit the form.

Try entering bigger numbers. Look at the stopwatch you created. What do you notice about the time?

Step 4: Generate an image in the worker thread

That was fun, but creating this image in the main thread froze our UI, sometimes for a few seconds, sometimes forever!

Let’s use our worker instead so that the the browser of the user does not blow up when creating the image.

First, let’s modify handleImageCreation to send messages to our worker:

function handleImageCreation(event) {
    event.preventDefault();
    const width = event.target[0].value;
    const height = event.target[1].value;
    worker.postMessage({ width, height });
}

Then, update receiveMessage to handle the image returned by the worker:

function receiveMessage(event) {
    console.log('Message received from worker thread', event.data);
    if (blob) {
        const imageData = URL.createObjectURL(blob);
        image.src = imageData;
    }
}

There’s some commented code in worker.js. Remove the /* and */.

And give it a try! What do you notice? Does the stopwatch freeze?

Part E: Bundle up your web worker

Well done! Want to level up some more? Learn how to use Webpack and React to bundle your web workers.