Многопоточность с помощью Web Worker в React

Javascript является однопоточным языком, независимо от того, какие асинхронные операции вы используете, такие как promises, async, await, settimeout и т.д., все они оказываются в точном стеке вызовов сейчас или позже.

Если вы когда-нибудь работали над корпоративными проектами, вы знаете, что основные проблемы – это оптимизация и производительность. Поэтому мы стараемся сделать пользовательский интерфейс настолько легким, насколько это возможно. Как я уже говорил выше, микрозадачи (async, await…) и макрозадачи (settimeout, setinterval…) не выполняются в отдельных потоках, они просто откладываются и выполняются позже в том же потоке пользовательского интерфейса.

В этой статье мы будем использовать API, доступный в браузерах и NodeJs, для достижения многопоточности в приложениях Javascript.

Итак, давайте начнем с настройки приложения React с помощью Typescript.

 npx create-react-app demo-app --template typescript
Вход в полноэкранный режим Выход из полноэкранного режима

Что такое веб-рабочие?

Веб-рабочие – это API, предоставляемые браузером/nodeJS, которые будут выносить ваши интенсивные задачи в отдельный поток и выполнять их. Связь между основным потоком и рабочим потоком осуществляется через postMessages(). Web Workers не имеют доступа к некоторым очень важным функциям JavaScript:

  • DOM (он не является потокобезопасным).
  • Объект окна.
  • Объект документа.
  • Объект родителя.

Теперь давайте создадим файл в папке src, где мы будем хранить весь интенсивный долго выполняющийся код.

Если вы видите ниже, мы используем postMessage() для связи с главным потоком и // eslint-disable-next-line no-restricted-globals, потому что self нельзя использовать в react без контекстных объектов.

intensiveTasks.ts

const workercode = () => {

  // eslint-disable-next-line no-restricted-globals
  self.onmessage = function (e) {
    const data = e.data
    // eslint-disable-next-line no-restricted-globals
    self.postMessage(data);
  }

  setTimeout(() => {
    // eslint-disable-next-line no-restricted-globals
    self.postMessage("Worker Thread: Hi");
  }, 2000);


};

let code = workercode.toString();
code = code.substring(code.indexOf("{") + 1, code.lastIndexOf("}"));

const blob = new Blob([code], { type: "application/javascript" });
const worker_script = URL.createObjectURL(blob);

export default worker_script;
Вход в полноэкранный режим Выход из полноэкранного режима

А здесь мы используем тот же postMessage() для связи с рабочим потоком, а myWorker.onmessage используется для получения сообщения обратно от рабочего потока.

App.tsx

import { useEffect, useRef, useState } from 'react';
import './App.css';
import IntensiveTask from './intensiveTask';

function App() {

  const [currentMessage, setCurrentMessage] = useState<string>('')
  const [allMessages, setAllMessages] = useState<string[]>([])

  const initiateWebWorker = () => {
    var myWorker = new Worker(IntensiveTask);

    // When you want to get messages from Worker Thread
    myWorker.onmessage = (message: any) => {
      setCurrentMessage(message.data)
    };

    // When you want to send messages to worker thread  
    myWorker.postMessage('Main Thread: Hello');
  }

  useEffect(() => initiateWebWorker(), [])

  useEffect(() => setAllMessages([...allMessages, currentMessage]), [currentMessage])

  return (
    <div className="App">
      <div className="App-header">
        <b>Web Worker Example</b>
        <p>{allMessages.map((data) => <div>{data}</div>)}</p>
      </div>
    </div>
  );
}

export default App;
Вход в полноэкранный режим Выход из полноэкранного режима

Вот и все! Теперь вы можете запустить наше приложение react с помощью команды npm start.

GitHub: https://github.com/blessonabraham/webworkers-react
Codesandbox: https://codesandbox.io/s/webworkers-react-8gvljs

Оцените статью
Procodings.ru
Добавить комментарий