๐Ÿ‘ทโ€โ™‚๏ธ<-Bob

Web Workers

What kind of ๐Ÿ‘ทโ€โ™‚๏ธ do we have?

  • Web ๐Ÿ‘ทโ€โ™‚๏ธย  (Bob)ย 

  • Sharedย  ๐Ÿ‘จโ€๐Ÿญ (Barry)

  • Service ๐Ÿ‘ท๐Ÿปโ€โ™‚๏ธ (John)

Web Worker (Bob ๐Ÿ‘ทโ€โ™‚๏ธ)ย 

  • It's a JavaScript file or a function that runs in the background, independently of other JavaScript files/functionsย 
  • Once created Bob does his assigned job and communicate with his "employer" about his task (e.g input,output of the job)

Bob is a basic ๐Ÿ‘ทโ€โ™‚๏ธ and everybody likes him

  • Communication between Bob and his employer is done with "postMessage" and it works both waysย 

What we need to hire Bob ?

Or:

if (window.Worker) {...}
else {console.log(
  "Your browser does not support workers๐Ÿ˜ข" );}

How to employ our Bob ?

Even easier

const myWorker = new Worker("worker.js");

HTML

<body>

    <form>

        <input type="text" id="number1" value="0">

        <input type="text" id="number2" value="0">

    </form>
	<p class="result">Result: 0</p>
  
</body>

<script src="./main.js"></script>

main.js

const first = document.querySelector('#number1');
const second = document.querySelector('#number2');
const result = document.querySelector('.result');

if (window.Worker) {
    const myWorker = new Worker("worker.js");
    first.onchange = function() {
      
      myWorker.postMessage([first.value, second.value]);
      console.log("Message sent to Bob");
      
    }
     second.onchange = function() {
       
      myWorker.postMessage([first.value, second.value]);
      console.log("Message sent to Bob");
       
    }
    myWorker.onmessage = function(e) {
      
        result.textContent = e.data;
        console.log("Message recived from Bob");
      
    }
} else {
  
    console.log("I am sorry you can't employ Bob ๐Ÿ˜ข")
  
}

Bob ๐Ÿ‘ทโ€โ™‚๏ธ (worker.js)

onmessage=function(e){

    console.log("Bob: I recive your message !");

    let result = e.data[0] * e.data[1];

    if (isNaN(result)) {

        postMessage('Bob: Give me 2 numbers !');

      } else {

        let workerResult = 'Result: ' + result;

        console.log('Bob: I sent you the result ');

        postMessage(workerResult);

      }

}

For what can we hire Bob ๐Ÿ‘ทโ€โ™‚๏ธ

Let's say for:ย 

Hard computing problems that would freeze or slow down our browser like a NN (Neural Network) Algorithm that has a lot of matrix multiplications or a ML (Machine learning)
console.log(trainingX.length)//50000
for( epochs=0; epochs<5;epochs++)
{
  for (i in trainingX)
	{
		output=InputMatrix*WeightMatrix+BiasVector;
		normalizedOutput=softmax(output)
		adjustWeightMatrix(threshhold-normalizedOut);
	}
}////etc..
// This is a NN example

Shared Worker (Barry ๐Ÿ‘จโ€๐Ÿญ)ย 

  • Barry is Bob's brother, it's a better Worker, Bob works for one "employer" when Barry works for more "employers" at the same time

What means that?ย 

  • One o more "employers" can ask Barry to do one job ( The same job)

The difference (in main.js)

worker= Worker('worker.js')

Web Worker (Bob)

Shared Worker (Bob)

var myWorker = new SharedWorker("shared-worker.js");
Bob can be called by only one employer at a time 
When Barry can be called by more at once 

The difference (in shared-worker.js)

postMessage(workerResult);
port.postMessage(workerResult);

The change

New

onconnect=function(e){
  
  var port=e.ports[0];
  
  port.onmessage=function(e){
    // Same code from Bob but with a little change ๐Ÿ˜‰
  }
}

HTML 1

<body>
  
    <h1 style="text-align: center;">Shared Worker</h1>
    <form>
      
        <input type="text" id="number1" value='0'>
      
        <input type="text" id="number2" value='0'>
      
    </form>
  
    <p class="result1">Rezultatul: 0</p>
 
    <a href="second-page.html">Second page</a>
  
</body>

<script src="./muliplication.js"></script>

multiplication.js

var first = document.querySelector('#number1');
var second = document.querySelector('#number2');
var result1 = document.querySelector('.result1');

if (!!window.SharedWorker) {
  
  var myWorker = new SharedWorker("shared-worker.js");

  first.onchange = function() {
    
    myWorker.port.postMessage([first.value, second.value]);
    console.log('Message posted to worker');
    
  }

  second.onchange = function() {
    
    myWorker.port.postMessage([first.value, second.value]);
    console.log('Message posted to worker');
    
  }

  myWorker.port.onmessage = function(e) {
    
    result1.textContent = e.data;
    console.log('Message received from worker');
    console.log(e.lastEventId);
    
  }
}

HTML 2

<body>
  
    <h1  style="text-align: center;">Second page</h1>
  
    <form>
      
        <input type="text" id='number3'>
      
        <p class="result2">Rezultatul: 0</p>
      
    </form>
  
    <a href="/Shared Worker/index.html">Back</a>
  
</body>

<script src="./squareRoot.js"></script>

squareRoot.js

var squareNumber = document.querySelector('#number3');

var result2 = document.querySelector('.result2');

if (!!window.SharedWorker) {
  
  var myWorker = new SharedWorker("shared-worker.js");

  squareNumber.onchange = function() {
    
    myWorker.port.postMessage([squareNumber.value, squareNumber.value]);
    
    console.log('Message posted to Barry');
  }

  myWorker.port.onmessage = function(e) {
    
    result2.textContent = e.data;
    
    console.log('Message received from Barry');
    
  }
}
shared-worker.js (Barry ๐Ÿ‘จโ€๐Ÿญ)
onconnect = function (e) {
    
    var port = e.ports[0];

    port.onmessage = function (e) {
    
        let result = e.data[0] * e.data[1];
    
        if (isNaN(result)) {
    
            port.postMessage('Barry:Give me 2 numbers');
    
        } else {
     
            let workerResult = 'Result: ' + result;
     
            port.postMessage(workerResult);
        }
    }
}

For what we can hire Bary ย ๐Ÿ‘จโ€๐Ÿญ

Not just helping us with some calculation like what Bob was doing but more with something like syncing 

One use case could be an online Photo/Video editor that can work on 2 or more tabs at the same time and ย with everything synchronized, like having 2 screens and moving the mouse around them but in browser

Service Worker ๐Ÿ‘ท๐Ÿปโ€โ™‚๏ธ(John)

John is a Worker, John is a service worker
He makes sure everything works just fine

How many times did you see this ?

AKA - They need to install and activate themselves and they are good to go 
Service workers can help you with that
But how ?
We need to register him

Install and Activate

Installation 

John is installing in the page then he waits to be activated and to work

Activation
After activation worker stays active even if we close/refresh the page, browser (on mobile too)

ย 

With other words, if John wants to work, you can't stop him  

How that works ?

Let's see how can we write a Service Worker with some code sample from the example 

Service Worker ๐Ÿ‘ท๐Ÿปโ€โ™‚๏ธ John

Here we are going to take things easy 
We call a Service Worker like this :
self.addEventListener('type', 'callback')
Note: I used " ' " to hightlight the "type" and "callback"

We are going to learn 3 basic types from the enventListener:

  • Install

ย 

  • Activate

ย 

  • Fetch

Install

This is used to install John in our page on first load
self.addEventListener('install',function(event){
  console.log("Install event: ", event);
})

Here we are going to find:

event.waitUntil(

caches.open('cache-name')
  .then(cache=> {
    return cache.addAll(
      [
        "elem_1",
        "elem_2"
      ])
  })
)

event.waitUntil()

Is telling that there is still work going on in the browser so he must wait until work is done 
event.waitUntil(
// Do something
)
caches.open('SW-store')
  .then(cache => {
      return cache.addAll([
        '/',
        "./main.js",
        "./index.html",
        "./1.jpg",
        "./2.jpg",
        "./3.jpg",
        "./4.jpg"
      ])
    })
cache.addAll([...])

What does this code ?

For each element of the list

(url item), stores the fetch data in caches with name "SW-store" under the url name (e.g. 

"./main.js":main.js(file)

)
Let's put it all together

self.addEventListener('install', function (event) {
  event.waitUntil(
    caches.open('service-worker-cache').then(cache => {
      return cache.addAll([
        '/',
        "./main.js",
        "./index.html",
        "./1.jpg",
        "./2.jpg",
        "./3.jpg",
        "./4.jpg"
      ])
    })
  )
  console.log('Install event:', event);
});

Activate

This is used to activate John in our page after the install
self.addEventListener('activate',function(event){
  console.log("Activate event: ", event);
})

Fetch

self.addEventListener('fetch', event => {
  console.log(event.request)
  event.respondWith(event.request);
});
What this mean? 
Whenever there is a request John hijack it and answer with what he wants or it's programmed to

Fetch

And he can give us the information that we need 
self.addEventListener('fetch', event => {
  console.log(event.request)
  event.respondWith(
      caches.match(event.request).then(response => {
          return response || fetch(event.request);
      })
  );
});

What is that code doing ?

That code sample is looking in caches memory for the request if we have it, return it else fetch the request 
Mini example structure:
This is what we need for a basic Service Worker that makes our page to load even if we are offline
We need:
  • 4 images
  • 1 html file 
  • 2 JS files

index.html

<body>
  
  <div class="gallery">
    
    <img src="1.jpg" alt="cat_img" >
    
    <img src="2.jpg" alt="cat_img" >
    
    <img src="3.jpg" alt="cat_img" >
    
    <img src="4.jpg" alt="cat_img" >
    
 </div>

</body>
  
<script src="main.js"></script>

main.js

if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('service-worker.js');
}
else {
    console.log("We are sorry John can not help you with that... ")
}

service-worker.js

self.addEventListener('install', function (event) {
  event.waitUntil(
    caches.open('service-worker-cache').then(cache => {
      return cache.addAll([
        '/',
        "./main.js",
        "./index.html",
        "./1.jpg",
        "./2.jpg",
        "./3.jpg",
        "./4.jpg"
      ])
    })
  )
  console.log('Install event:', event);
});
self.addEventListener('activate', function (event) {
  console.log('Activate event:', event);
});
self.addEventListener('fetch', event => {
  console.log(event.request)
  event.respondWith(
      caches.match(event.request).then(response => {
          return response || fetch(event.request);
      })
  );
});

Let's test it

We need VSCode Live Server for this
Right click in the index.html file in VS Code and look for "Open with Live Server" and click it
Your main browser should open with url: "127.0.0.1:5500" and show the images from your folder
Now press "Ctrl +Shit+i" to inspect the page. Then go to console where you should see something like this 
Next step:

Go to "Application" look for "Service Worker" and click on itย 

Check the "Offline" button and refresh the page

What happened ?

Okey let's try to look in the caches 
Under the "Service Worker" there is a tab called "Caches Storage, let click it

It should look like thisย 

Let's look inside one 
Try to click one 
Congrats you just build you first offline web page !!!        ๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰

Advantages of Web Workers ๐Ÿ‘ทโ€โ™‚๏ธ

You can run tasks on other threads without affecting browser speed and user interactions 
They are easy to make. Hire your own Bob today, it's free!
They can hold your website even offline 

One exercise

Create a worker that fetch for you a 'n' number dog images. With the information from the worker display on the screen the images
Let's use this API: https://dog.ceo/dog-api/

For those who forgot or don't know how to use fetch here is a little example:

fetch("https://dog.ceo/api/breeds/image/random/3")
.then(res=>res.json())
.then(resJson=>{
//   what you want to do 
})
.catch(err=>{
  console.log("Error: ",err)
})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <input type="number" name="" id="number" value=0>
    <button id="fetch-dogs">Give me dogs</button>
    <div class="dog-space"></div>
</body>
<script src="main.js"></script>
</html>

Links:

Thank you for your attention !

P.S. Bob was happy to meet you ๐Ÿ‘ทโ€โ™‚๏ธ

Web Workers

By Cristian GATU

Web Workers

A small introduction into Web Workers

  • 116
Loading comments...