Веб-воркеры необходимы в веб-разработке, поскольку они позволяют разработчикам выполнять ресурсоемкие или трудоемкие задачи в фоновом режиме, отдельно от основного потока JavaScript.
Перекладывая эти задачи на веб-работников, основной поток остается отзывчивым, обеспечивая бесперебойную работу пользователей.
Веб-воркеры обеспечивают параллельное выполнение, повышая производительность за счет использования возможностей параллельной обработки. Эта возможность повышает общую скорость отклика и эффективность веб-приложений, делая их способными выполнять сложные вычисления и ресурсоемкие операции, не влияя на пользовательский интерфейс.
В нашем примере мы попытаемся создать общий (универсальный) рабочий процесс, который будет выполнять любую функцию, которую мы передаем ему с заданной полезной нагрузкой.
1) Базовый пример
index.html
<!DOCTYPE html> <html> <head> <title>Web Worker Example</title> </head> <body> <h1>Web Worker Example</h1> <button onclick="runWorker()">Run Web Worker</button> <script> // Create a new web worker const worker = new Worker('worker.js'); // Send payload and function to the web worker const payload = { message: 'Hello, worker!' }; const functionToExecute = (data) => { console.log(`Received payload in the worker: ${data.message}`); }; worker.postMessage({ payload, functionToExecute }); </script> </body> </html>
У нас есть только кнопка на этой странице, которая будет выполнять функцию runWorker (которую мы создадим) по щелчку.
Теперь нам нужно создать веб-воркер для запуска любой функции.
worker.js
// Listen for messages from the main script self.onmessage = function(event) { const { fn, data } = event.data; // Execute the provided function with the given data const result = fn(data); // Send the result back to the main script self.postMessage(result); };
В этом примере есть одна проблема. Нельзя передавать/отправлять Функции воркерам!
Обходной путь состоит в том, чтобы преобразовать вашу чистую функцию в строку и передать ее в качестве аргумента:
index.html
<!DOCTYPE html> <html> <head> <title>Web Worker Example</title> </head> <body> <h1>Web Worker Example</h1> <button onclick="runWorker()">Run Web Worker</button> <script> function runWorker() { // Create a new web worker const worker = new Worker('worker.js'); // Send payload and function to the web worker const data = [1, 2, 3, 4, 5]; // Function to be executed in the web worker const sumArray = (arr) => { return arr.reduce((sum, num) => sum + num, 0); }; worker.postMessage({ payload, functionToExecute: sumArray.toString() }); } </script> </body> </html>
worker.js
// Listen for messages from the main script self.onmessage = function (event) { const { fn, data } = event.data; // Execute the provided function with the given data const result = new Function("return " + fn)()(data); // Send the result back to the main script self.postMessage(result); };
2) Избавление от файла worker.js (с рабочим примером)
Создание файла worker.js и его импорт — это большая работа :)
Передача имени файла конструктору Worker — не единственный способ создать веб-воркер. Мы можем передать в него javascript, закодированный в base-64 (для этого примера будет закодирован только console.log(''hello world'):
Вы можете использовать эту ссылку, чтобы закодировать собственный код JavaScript http://base64online.org/encode/
index.html
<!DOCTYPE html> <html> <head> <title>Web Worker Example</title> </head> <body> <h1>Web Worker Example</h1> <button onclick="runWorker()">Run Web Worker</button> <script> function runWorker() { // Create a new web worker with Encoded JS // self.onmessage = () => console.log("hello world") const worker = new Worker( "data:application/x-javascript;base64,c2VsZi5vbm1lc3NhZ2UgPSAoKSA9PiBjb25zb2xlLmxvZygiaGVsbG8gd29ybGQiKQo=" ); // Send payload and function to the web worker const data = [1, 2, 3, 4, 5]; // Function to be executed in the web worker const sumArray = (arr) => { return arr.reduce((sum, num) => sum + num, 0); }; worker.postMessage({ payload: data, functionToExecute: sumArray.toString(), }); } </script> </body> </html>
3) Выполнение функции и полезной нагрузки в воркере
Давайте изменим наш worker, чтобы на этот раз он выполнял нашу функцию и возвращал значение, теперь мы закодируем этот кусок в worker:
self.onmessage = function (event) { const { functionToExecute, payload } = event.data; const result = new Function("return " + functionToExecute)()(payload); self.postMessage(result); };
index.html
<!DOCTYPE html> <html> <head> <title>Web Worker Example</title> </head> <body> <h1>Web Worker Example</h1> <button onclick="runWorker()">Run Web Worker</button> <script> function runWorker() { // Create a new web worker with Encoded JS // self.onmessage = () => console.log("hello world") const worker = new Worker( "data:application/x-javascript;base64,c2VsZi5vbm1lc3NhZ2UgPSBmdW5jdGlvbiAoZXZlbnQpIHsKICBjb25zdCB7IGZ1bmN0aW9uVG9FeGVjdXRlLCBwYXlsb2FkIH0gPSBldmVudC5kYXRhOwogIGNvbnN0IHJlc3VsdCA9IG5ldyBGdW5jdGlvbigicmV0dXJuICIgKyBmdW5jdGlvblRvRXhlY3V0ZSkoKShwYXlsb2FkKTsKICBzZWxmLnBvc3RNZXNzYWdlKHJlc3VsdCk7Cn07" ); // Send payload and function to the web worker const data = [1, 2, 3, 4, 5]; // Function to be executed in the web worker const sumArray = (arr) => { return arr.reduce((sum, num) => sum + num, 0); }; worker.postMessage({ payload: data, functionToExecute: sumArray.toString(), }); } </script> </body> </html>
Хорошо, теперь мы отправляем нашу функцию и полезную нагрузку в worker, и она выполняется, но мы не получаем результат обратно.
4) Получение результата через обещание (рабочий пример TLDR)
Мы можем превратить эту часть в функцию, основанную на промисах, чтобы упростить ее использование.
<!DOCTYPE html> <html> <head> <title>Web Worker Example</title> </head> <body> <h1>Web Worker Example</h1> <button onclick="runWorker()">Run Web Worker</button> <script> // Create a new web worker const myWorker = new Worker( "data:application/x-javascript;base64,c2VsZi5vbm1lc3NhZ2UgPSBmdW5jdGlvbiAoZXZlbnQpIHsKICBjb25zdCB7IGZ1bmN0aW9uVG9FeGVjdXRlLCBwYXlsb2FkIH0gPSBldmVudC5kYXRhOwogIGNvbnN0IHJlc3VsdCA9IG5ldyBGdW5jdGlvbigicmV0dXJuICIgKyBmdW5jdGlvblRvRXhlY3V0ZSkoKShwYXlsb2FkKTsKICBzZWxmLnBvc3RNZXNzYWdlKHJlc3VsdCk7Cn07" ); async function runWorker() { const data = [1, 2, 3, 4, 5]; // Function to be executed in the web worker const sumArray = (arr) => { return arr.reduce((sum, num) => sum + num, 0); }; // Execute the function in the web worker const result = await executeFunctionInWorker(sumArray, data); console.log("Result from web worker:", result); } async function executeFunctionInWorker(functionToExecute, payload) { // Create a Promise to handle the worker's response const workerResponse = new Promise((resolve) => { // Listen for messages from the worker myWorker.onmessage = function (event) { resolve(event.data); }; }); // Post a message to the worker with the function and data myWorker.postMessage({ functionToExecute: functionToExecute.toString(), payload, }); // Wait for the worker's response const response = await workerResponse; return response; } </script> </body> </html>
В этом примере показано, как можно использовать функцию executeFunctionInWorker()
для выполнения определенной функции в веб-воркере, передачи ему данных и асинхронного получения результата.
Вы можете поднять эту реализацию еще на один уровень и присвоить идентификатор каждому сообщению и даже превратить его в исполняющего исполнителя на основе очереди.
Использованная литература: