Веб-воркеры необходимы в веб-разработке, поскольку они позволяют разработчикам выполнять ресурсоемкие или трудоемкие задачи в фоновом режиме, отдельно от основного потока 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() для выполнения определенной функции в веб-воркере, передачи ему данных и асинхронного получения результата.

Вы можете поднять эту реализацию еще на один уровень и присвоить идентификатор каждому сообщению и даже превратить его в исполняющего исполнителя на основе очереди.

Использованная литература:



https://www.w3schools.com/html/html5_webworkers.asp