Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Web Workers w React

Wyobraz sobie, ze na Twoim statku kosmicznym glowny komputer pokladowy (main thread) musi jednoczesnie renderowac kokpit, obslugiwac systemy nawigacyjne i przeliczac trajektorie lotu. Jesli obliczenia trajektorii sa zbyt ciezkie, kokpit zamraza sie - kapitan nie moze kliknac zadnego przycisku. Rozwiazanie? Dedykowany komputer obliczeniowy (Web Worker), ktory pracuje rownolegle, nie blokujac glownego systemu.

Czym sa Web Workers?

Web Workers to mechanizm przegladarki, ktory pozwala uruchamiac kod JavaScript w oddzielnym watku (thread). Glowny watek (main thread) odpowiada za rendering UI, obsluge eventow i interakcje uzytkownika. Gdy wykonujesz ciezkie obliczenia na main thread, interfejs sie "zamraza".

1// BEZ Web Worker - blokuje main thread
2function HeavyCalculation() {
3  const [result, setResult] = useState(null);
4
5  const calculate = () => {
6    // To zablokuje UI na kilka sekund!
7    let sum = 0;
8    for (let i = 0; i < 1_000_000_000; i++) {
9      sum += Math.sqrt(i);
10    }
11    setResult(sum);
12  };
13
14  return (
15    <div>
16      <button onClick={calculate}>Oblicz</button>
17      <p>Wynik: {result}</p>
18    </div>
19  );
20}

Tworzenie Web Workera

Web Worker to oddzielny plik JavaScript, ktory komunikuje sie z glownym watkiem przez system wiadomosci (

postMessage
/
onmessage
):

1// worker.js - plik Workera (oddzielny watek)
2self.onmessage = function(event) {
3  const { type, data } = event.data;
4
5  if (type === 'CALCULATE_TRAJECTORY') {
6    const result = calculateTrajectory(data);
7    self.postMessage({ type: 'RESULT', result });
8  }
9};
10
11function calculateTrajectory(params) {
12  let trajectory = [];
13  for (let t = 0; t < params.steps; t++) {
14    const x = params.speed * t * Math.cos(params.angle);
15    const y = params.speed * t * Math.sin(params.angle) - 0.5 * 9.81 * t * t;
16    trajectory.push({ x, y, t });
17  }
18  return trajectory;
19}
1// App.jsx - komponent React (glowny watek)
2import { useState, useRef, useEffect } from 'react';
3
4function TrajectoryCalculator() {
5  const [result, setResult] = useState(null);
6  const [isCalculating, setIsCalculating] = useState(false);
7  const workerRef = useRef(null);
8
9  useEffect(() => {
10    workerRef.current = new Worker(new URL('./worker.js', import.meta.url));
11
12    workerRef.current.onmessage = (event) => {
13      if (event.data.type === 'RESULT') {
14        setResult(event.data.result);
15        setIsCalculating(false);
16      }
17    };
18
19    return () => workerRef.current?.terminate();
20  }, []);
21
22  const handleCalculate = () => {
23    setIsCalculating(true);
24    workerRef.current.postMessage({
25      type: 'CALCULATE_TRAJECTORY',
26      data: { speed: 1000, angle: 0.785, steps: 100000 }
27    });
28  };
29
30  return (
31    <div>
32      <button onClick={handleCalculate} disabled={isCalculating}>
33        {isCalculating ? 'Obliczam...' : 'Oblicz trajektorie'}
34      </button>
35      {result && <p>Punktow trajektorii: {result.length}</p>}
36    </div>
37  );
38}

Custom Hook: useWorker

Aby uniknac powtarzania logiki tworzenia i zarzadzania Workerami, mozemy stworzyc reusable hook:

1import { useState, useRef, useEffect, useCallback } from 'react';
2
3function useWorker(workerFactory) {
4  const [result, setResult] = useState(null);
5  const [error, setError] = useState(null);
6  const [isRunning, setIsRunning] = useState(false);
7  const workerRef = useRef(null);
8
9  useEffect(() => {
10    workerRef.current = workerFactory();
11
12    workerRef.current.onmessage = (event) => {
13      setResult(event.data);
14      setIsRunning(false);
15    };
16
17    workerRef.current.onerror = (err) => {
18      setError(err.message);
19      setIsRunning(false);
20    };
21
22    return () => workerRef.current?.terminate();
23  }, []);
24
25  const run = useCallback((data) => {
26    setIsRunning(true);
27    setError(null);
28    workerRef.current?.postMessage(data);
29  }, []);
30
31  return { result, error, isRunning, run };
32}

Comlink - uproszczona komunikacja

Biblioteka Comlink (od Google Chrome Labs) pozwala traktowac Web Workers jak zwykle obiekty z async metodami:

1// worker.js z Comlink
2import * as Comlink from 'comlink';
3
4const navigationComputer = {
5  async calculateRoute(origin, destination) {
6    const route = computeOptimalPath(origin, destination);
7    return route;
8  },
9  async analyzeStarField(stars) {
10    return stars.map(star => ({
11      ...star,
12      habitableZone: calculateHabitableZone(star.mass, star.luminosity)
13    }));
14  }
15};
16
17Comlink.expose(navigationComputer);
1// App.jsx z Comlink
2import * as Comlink from 'comlink';
3
4function NavigationPanel() {
5  const [route, setRoute] = useState(null);
6  const navComputer = useRef(null);
7
8  useEffect(() => {
9    const worker = new Worker(new URL('./worker.js', import.meta.url));
10    navComputer.current = Comlink.wrap(worker);
11    return () => worker.terminate();
12  }, []);
13
14  const planRoute = async () => {
15    // Wywolujemy metode jak zwykla funkcje async!
16    const result = await navComputer.current.calculateRoute(
17      { x: 0, y: 0, z: 0 },
18      { x: 4.2, y: 1.3, z: 0.7 }
19    );
20    setRoute(result);
21  };
22
23  return (
24    <div>
25      <button onClick={planRoute}>Planuj trase</button>
26      {route && <RouteVisualization data={route} />}
27    </div>
28  );
29}

Kiedy uzywac Web Workers w React?

Web Workers sa idealne do:

  • Ciezkich obliczen matematycznych - symulacje, analiza danych
  • Parsowania duzych zbiorow danych - CSV, JSON z tysiasami rekordow
  • Przetwarzania obrazow - filtry, kompresja
  • Wyszukiwania i filtrowania - fuzzy search w duzych zbiorach

Web Workers NIE sa potrzebne do:

  • Prostych operacji na stanie
  • Zapytan API (fetch/axios)
  • Manipulacji DOM (Workers nie maja dostepu do DOM)

Podsumowanie

  1. Web Workers - oddzielny watek dla ciezkich obliczen
  2. Komunikacja -
    postMessage
    /
    onmessage
  3. Custom hook
    useWorker
    - enkapsulacja logiki Workera
  4. Comlink - uproszczone API (async metody zamiast wiadomosci)
  5. Ograniczenia - brak dostepu do DOM, osobny scope
Vai a CodeWorlds