๐ทโโ๏ธ<-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
- 623