https://en.wikipedia.org/wiki/Multithreading_(computer_architecture)In computer architecture, multithreading is the ability of a central processing unit (CPU) (or a single core in a multi-core processor) to provide multiple threads of execution.
Web browsers are complex applications that handle a variety of tasks simultaneously
Web browsers employ a hybrid approach to thread management.
๐ฃ Single-Threaded: JavaScript execution on the main thread, managed by an event loop.
๐ฃ Multi-Threaded: Use of Web Workers for background tasks, and multi-threaded browser engine components for rendering, networking, and GPU acceleration.
const heavyTask = () => {
for (let i = 0; i < 10000000000; i++) {
//do something useful
}
};
self.onmessage = (event) => {
console.log("Worker received:", event.data);
// Do heavy stuff
self.postMessage(`Hello from worker!`);
};
const worker = new Worker("worker.js");
worker.onmessage = (event) => { console.log("Main thread received:", event.data); // Do some other stuff };
// Starting point worker.postMessage("Hello Web Day!");
self.onmessage = (event) => {};
self.onmessage = (event) => {};
postMessage(message, transfer)
postMessage(message, transfer)
๐ฌ message
๐จ will be in the data field in the message object
๐ฉ required
๐ transfer
๐ only transferable objects like ArrayBuffer or ImageBitmap
๐ถโ๐ซ๏ธ optional
worker.onerror = (event) => {};
worker.onerror = (event) => {};
worker.onerror = (event) => {
console.log(event.message);
};
const clients = [];
self.onconnect = (event) => {
const [port] = event.ports;
clients.push(port);
port.postMessage("Worker connected");
port.onmessage = (e) => {
clients.forEach((client) =>
client.postMessage(
`New client connected with message: "${e.data}"`
)
);
};
};
const sharedWorker = new SharedWorker("shared-worker.js");
sharedWorker.port.start();
sharedWorker.port.onmessage = (e) => {
console.log(e.data);
};
sharedWorker.port.postMessage("Hello from Tab 1");
const clients = [];
self.onconnect = (event) => {
// ...other stuff
port.onclose = () => {
clients.splice(clients.indexOf(port), 1);
clients.forEach((client) =>
client.postMessage(
`Client disconnected. Total clients remaining: ${clients.length}`
)
);
};
}
window
, document
, or
parent
alert()
, confirm()
, or
prompt()
ng g web-worker services/worker-test
CREATE src/app/services/worker-test.worker.ts (157 bytes)
CREATE tsconfig.worker.json (334 bytes)
UPDATE angular.json (2946 bytes)
UPDATE src/app/services/worker-test.service.ts (537 bytes)
/// <reference lib="webworker" />
addEventListener('message', ({ data }) => {
const response = `worker response to ${data}`;
postMessage(response);
});
// ... export class WorkerTestService { constructor() { } }
if (typeof Worker !== 'undefined') { // Create a new const worker = new Worker(new URL('./worker-test.worker', import.meta.url));
worker.onmessage = ({ data }) => { console.log(`page got message: ${data}`); }; worker.postMessage('hello');
} else { // Web Workers are not supported in this environment. // You should add a fallback so that your program still executes correctly. }
addEventListener('message', ({ data }) => {
heavyTask();
postMessage(`done "${data}" task`);
});
//... export class WorkerTestService { #worker!: Worker; constructor() { this.createWorker(); } createWorker() { this.#worker = new Worker( new URL('./worker-test.worker.ts', import.meta.url) ); }
doHeavyWork(data: string): Observable<string> { return new Observable((observer) => { this.#worker.onmessage = ({ data }: MessageEvent<string>) => { observer.next(data); observer.complete(); }; this.#worker.onerror = (error) => observer.error(error); this.#worker.postMessage(data); }); } }
// ...
export class HomeComponent {
readonly #workerService = inject(WorkerTestService);
startHeavyWorkInWorker() {
this.#workerService
.doHeavyWork('daje')
.subscribe((data) => console.log(data)); // done "daje" task
}
}
const POOL_SIZE = 4; export class WorkerPoolService { #workers: Worker[] = []; constructor() { this.createPoolWorkers(); }
createPoolWorkers() { for (let i = 0; i < POOL_SIZE; i++) { const worker = new Worker( new URL('./worker-test.worker.ts', import.meta.url) ); this.#increaseWorkerPool(worker); } }
#increaseWorkerPool(worker: Worker) { this.#workers.push(worker); } }
export class WorkerPoolService {
//...
getWorker(data: string): Observable<string> | null {
const worker = this.#workers.pop();
if (!worker) return null;
return new Observable((observer) => {
worker.onmessage = ({ data }: MessageEvent<string>) => {
observer.next(data);
observer.complete();
this.#increaseWorkerPool(worker);
};
worker.onerror = (error) => observer.error(error);
worker.postMessage(data);
});
}
}
export class PoolWorkerComponent {
//...
startNewHeavyJob(task: string) {
const newWorker = this.#poolWorkerService.getWorker(task);
if (!newWorker) {
this.disableButton.set(true);
return;
}
newWorker.subscribe((data) => {
this.disableButton.set(false);
console.log(data);
});
}
}
self.onmessage = async (event) => {
const { data: question } = event;
const answer = await answer(question);
self.postMessage(`The answer is: ${answer}`);
};