DEV Community

Cover image for Introducing Javascript Web Workers
Kevin Toshihiro Uehara
Kevin Toshihiro Uehara

Posted on

Introducing Javascript Web Workers

Hi, people!! How you doing?
It's nice to have you again!

In this artile I want to introduce you about Javascript Web Workers!

The idea to create this article came out after the creation of an application that I created through collaboration and "dispute" here in Brazil, called "Rinha Front end".

The main ideia of this app is read some json's files on client side. But you may ask yoursef: ok... so what is the problem?
And I answer you: Performance.

The project contains a lot of json's to read, process and display on screen. But 8kb it's ok. But if I try to read and process 200mb json file on client side, display on screen, without broke the browser?

Yeah, the process might be hard to implement and how I can create some script to read this large file, without broke the browser or make the Web Vitals be perfect?

First I think to broke this file on chunks, read, parse the JSON and after that I displayed on screen. Worked, but the browser was withstanding the amount of data to be rendered, as it was applying everything at once.

So on my implementation I used FileReader API to transform data on bytes and splice on chunks and process with Web Workers. So finally, I managed to finish the application.

In another article, I can bring the implementation and how to work with chunks on Javascript to process large data.
But now, lets talk about WEB WORKERS.

In this article I want to introduce the web workers and how we can add performance in our application, without blocking the interface interaction.

Introduction

We always think that Vanilla Javascript is Single-thread, and yes (we are not talking about Node that use Libuv - maybe another artile lol).
And yep, JS vanilla is single thread, but we know how it works, can we? Event Loop, Callback Queue, Stack and more...

If you don't now about what I'm talking, I recommend you to read the artile where I talk about how JS engine v8 works:

The article: https://dev.to/kevin-uehara/how-javascript-v8-engine-works-pdp

But some API's was introduced on our beauty JS, the web workers. We can simulate a multi-thread application. We can process data on background "workers" while the UI is not blocked when the processing some large operations.

After the process finish, the worker returns the value to the main thread and this way we can work with large processes without blocking the interface.

Web Workers Image

Show me the code

Let's create some example and I will demonstrate a application without and with web workers.

In this application we will create a function that will iterate 1e8 * 30 or 4500000000067114000 value and simply return this value. This function will be trigged by the first button.

The second button will trigger a function that will simply change the color the background to black or white.

Now Let's create our index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Worker Example</title>
    <link rel="stylesheet" href="style.css">
    <script src="index.js" async></script>
</head>
<body>
    <button class="btn" id="btn-large-operation">Start Large Operation</button>
    <button class="btn" id="btn-change-color">Change Background Color</button>
    <pre id="output"></pre>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Now let's create our style.css:

body {
    margin: 0;
    padding: 0;
    font-family: Arial, Helvetica, sans-serif;
    box-sizing: border-box;
    background-color: #fff;
}

.btn {
    padding: 12px;
    background-color: #3730a3;
    cursor: pointer;
    color: #fff;
    border-radius: 4px;
    border: none
}

.btn:hover {
    background-color: #6366f1;
}
Enter fullscreen mode Exit fullscreen mode

Annnnd now our Javascript file index.js:

const btnLargeOperation = document.getElementById("btn-large-operation");
const btnChangeColor = document.getElementById("btn-change-color");
const output = document.getElementById("output");

const handleLargeOperation = () => {
  let value = 0;
  for (let i = 0; i <= 1e8 * 30; i++) {
    value += i;
  }
  return value;
};

btnLargeOperation.addEventListener("click", () => {
  output.textContent = "Started Large Process...";
  const value = handleLargeOperation();
  output.textContent = value;
  console.log(value);
});

// This event will be blocked by LargeOperation without web worker
btnChangeColor.addEventListener("click", () => {
  const body = document.querySelector("body");
  if (
    body.style.backgroundColor &&
    body.style.backgroundColor !== "rgb(255, 255, 255)"
  ) {
    body.style.backgroundColor = "#fff";
  } else {
    body.style.backgroundColor = "#000";
  }
});
Enter fullscreen mode Exit fullscreen mode

Now I will run this application using live-server, and let's see what will happens:

Gif of app wihout web workers

We can see, that after I clicked on Start Large Operation, I can’t change the background color. The UI is blocked because of the large process of the function.

To change this performance and add work this process on background as multi-thread simulation, let's create the file called worker.js:

const handleLargeOperation = () => {
  let value = 0;
  for (let i = 0; i <= 1e8 * 30; i++) {
    value += i;
  }
  return value;
};

onmessage = (event) => {
  if (event.data === "operation") {
    const value = handleLargeOperation();
    postMessage(value);
  }
};
Enter fullscreen mode Exit fullscreen mode

We will move that function to this file and we create this listener function that will be subscribe on each message called on our index.js.

Now let's change our 'index.js':

const btnLargeOperation = document.getElementById("btn-large-operation");
const btnChangeColor = document.getElementById("btn-change-color");
const output = document.getElementById("output");

btnLargeOperation.addEventListener("click", () => {
  output.textContent = "Started Large Process...";
  //   const value = handleLargeOperation();
  //   console.log(value);
  const worker = new Worker("worker.js");
  const before = Date.now();
  worker.postMessage("operation");
  worker.onmessage = (event) => {
    const after = Date.now();
    console.log("Executed in: ", (after - before) / 1000, " s");
    console.log("Data processed Received: ", event.data);
    output.textContent = event.data;
  };
});

// This event will be blocked by LargeOperation without web worker
btnChangeColor.addEventListener("click", () => {
  const body = document.querySelector("body");
  if (
    body.style.backgroundColor &&
    body.style.backgroundColor !== "rgb(255, 255, 255)"
  ) {
    body.style.backgroundColor = "#fff";
  } else {
    body.style.backgroundColor = "#000";
  }
});
Enter fullscreen mode Exit fullscreen mode

We intenciate the worker passing the file location using:

const worker = new Worker("worker.js");
Enter fullscreen mode Exit fullscreen mode

And we can use the functions worker.postMessage to send a event that the onmessage of worker will receive.
And we have the worker.onmessage that will receive any data that the worker returns;

Nowwww, let's see what happens:

Gif of app using web worker

Woooow! Amazing, isn’t ?

Now our application is not blocking the interface interactions and the large process will be processed on background. After the process and the web worker function returns to us the value processed.

And now we can understand how web workers works;

Understand meme

That's all folks!
So I hope that you like it.

Contacts:
Email: uehara.kevin@gmail.com
Linkedin: https://www.linkedin.com/in/kevin-uehara
Youtube: https://www.youtube.com/@ueharakevin/
Instagram: https://www.instagram.com/uehara_kevin/
Twitter: https://twitter.com/ueharaDev
Github: https://github.com/kevinuehara
dev.to: https://dev.to/kevin-uehara

Top comments (0)