About Me
En

/

Vi
Sử dụng Web Workers để xử lý tác vụ nặng trong trình duyệt

Sử dụng Web Workers để xử lý tác vụ nặng trong trình duyệt

Tìm hiểu cách Web Workers giúp tách tác vụ nặng ra khỏi luồng chính nhằm tăng hiệu suất và trải nghiệm người dùng.

Giới thiệu

Trong các ứng dụng web hiện đại, hiệu suất là một yếu tố quan trọng ảnh hưởng trực tiếp đến trải nghiệm người dùng. Một trong những nguyên nhân chính gây ra tình trạng giật lag, hoặc block UI là do các tác vụ tính toán nặng (heavy tasks) được thực thi trực tiếp ngay trên (main thread).

Để giải quyết vấn đề này, JavaScript cung cấp giải pháp Web Workers, cho phép chạy các đoạn mã song song trong một luồng riêng biệt, giúp tránh xử lí trên main threadtăng hiệu suất ứng dụng.

1. Web Workers là gì?

Web Workers là một API của trình duyệt cho phép bạn thực thi JavaScript trong một luồng riêng biệt với luồng chính của ứng dụng.

  • Web Workers có thể giao tiếp với main thread thông qua postMessage.
  • Phù hợp để xử lý: tính toán nặng, phân tích dữ liệu, xử lý ảnh, nén file,...

2. Tại sao nên sử dụng Web Workers?

Lợi ích:

  • Không làm block UI: vì không chạy trên luồng chính.
  • Cải thiện UX: giao diện vẫn phản hồi trong khi xử lý tác vụ.
  • Tăng hiệu suất cho các tác vụ tính toán phức tạp.

Khi không sử dụng Web Workers:

  • UI dễ bị đơ khi xử lý JSON lớn, vòng lặp nhiều, hoặc xử lí thuật toán nặng.
  • User có thể nhầm tưởng ứng dụng bị crash nếu không phản hồi.

3. Ví dụ về cách sử dụng Web Workers cho bài toán tính giai thừa

Giả sử bạn cần thực hiện phép tính giai thừa (n!) với các số lớn – đây là một tác vụ có thể gây chậm hoặc "đơ" giao diện nếu xử lý trực tiếp trong main thread. Web Worker sẽ giúp tách việc xử lý ra luồng riêng, đảm bảo UI luôn mượt mà.

Chúng ta sẽ xây dựng một ví dụ đơn giản gồm:

  • Một file Web Worker để tính giai thừa.
  • Một component React (TSX) gồm ô input, button nhấn để gửi dữ liệu vào worker, và các thẻ p để hiển thị kết quả.

🧮 factorialWorker.ts – File worker

self.onmessage = function (e) { const number = e.data; const result = factorial(number); self.postMessage(result); }; function factorial(n: number): BigInt { let result = BigInt(1); for (let i = 2; i <= n; i++) { result *= BigInt(i); } return result; } export {};

🧑‍💻 FactorialCalculator.tsx – React component

import React, { useState, useRef } from "react"; const FactorialCalculator: React.FC = () => { const [input, setInput] = useState<number>(100000); const [result, setResult] = useState<number | null>(null); const [loading, setLoading] = useState(false); const [totalTime, setTotalTime] = useState(0); const workerRef = useRef<Worker | null>(null); const handleCalculate = () => { setResult(null); setTotalTime(0); setLoading(true); const startTime = performance.now(); // Start timing const worker = new Worker(new URL("@src/worker/factorialWorker", import.meta.url)); workerRef.current = worker; worker.postMessage(input); worker.onmessage = (e: MessageEvent<number>) => { const endTime = performance.now(); // End timing setTotalTime(endTime - startTime); // Calculate total time in milliseconds setResult(e.data); setLoading(false); worker.terminate(); }; }; return ( <div> <input type="number" value={input} onChange={(e) => setInput(Number(e.target.value))} placeholder="Nhập số nguyên dương" /> <button onClick={handleCalculate} disabled={loading}> {loading ? "Đang tính..." : "Tính giai thừa"} </button> {result !== null && <p>Kết quả: {result}</p>} {totalTime > 0 && <p>Thời gian tính toán: {totalTime.toFixed(3)} ms</p>} </div> ); }; export default FactorialCalculator;

Kết quả:

Khi không sử dụng Web WorkerKhi sử dụng Web Worker
Cách hoạt động của Web Worker
Cách hoạt động của Web Worker

Ta có thể thấy khi sử dụng web-worker, UI mượt mà hơn rất nhiều so với khi không sử dụng nó.

4. Lưu ý khi sử dụng web worker

  • Không thể truy cập đến DOM từ trong worker file.
  • Đảm bảo luồng giao tiếp postMessage rõ ràng và không gây race condition.
  • Có thể dùng Comlink để đơn giản hóa giao tiếp.

5. Khi nào nên dùng Web Workers?

Tình huốngCó nên dùng Worker?
Tính toán nặng như mã hóa, nén file✅ Cần
Xử lý file JSON lớn (>1MB)✅ Cần
Cập nhật UI đơn giản❌ Không cần
Fetch API, gọi server❌ Không cần (dùng async/await cho lẹ :D)

6. Các option nâng cao khác

  • SharedWorker: chia sẻ worker giữa nhiều tab.
  • Service Worker: xử lý request, cache – khác với Web Worker.
  • OffscreenCanvas: render canvas trong worker (đặc biệt hữu ích với đồ họa).

Kết luận

Web Workers là công cụ mạnh mẽ giúp cải thiện hiệu suất của các ứng dụng web hiện đại. Việc tách biệt các tác vụ nặng ra khỏi luồng chính sẽ giúp UI luôn mượt mà, thân thiện với người dùng. Hãy cân nhắc sử dụng Web Workers khi bắt đầu thấy trình duyệt bị "lag" khi xử tác vụ phức tạp.

Using Web Workers to Handle Heavy Tasks in the Browser

Learn how Web Workers help offload heavy computations from the main thread to improve performance and user experience.

Introduction

In modern web applications, performance is a critical factor that directly affects the user experience. One of the main causes of lagging or UI blocking is due to heavy tasks being executed directly on the main thread.

To address this issue, JavaScript provides the Web Workers API, which allows running code in parallel in a separate thread, helping avoid blocking the main thread and improving application performance.

1. What are Web Workers?

Web Workers are a browser API that allows you to execute JavaScript in a separate thread from the main application thread.

  • Web Workers can communicate with the main thread via postMessage.
  • Ideal for handling: heavy computation, data analysis, image processing, file compression, etc.

2. Why use Web Workers?

Benefits:

  • No UI blocking: since it doesn't run on the main thread.
  • Better UX: the UI remains responsive while the task is being processed.
  • Improved performance for complex calculations.

Without Web Workers:

  • The UI can easily freeze when processing large JSON files, long loops, or complex algorithms.
  • Users may think the app crashed if there's no response.

3. Example: Using Web Workers to Calculate Factorial

Suppose you need to calculate factorial (n!) of large numbers — this can be a task that slows down or freezes the UI if processed on the main thread. Web Workers help offload this task to a separate thread, ensuring a smooth UI.

We’ll build a simple example including:

  • A Web Worker file to calculate factorial.
  • A React (TSX) component with an input box, a button to send data to the worker, and <p> tags to display the result.

🧮 factorialWorker.ts – Worker file

self.onmessage = function (e) { const number = e.data; const result = factorial(number); self.postMessage(result); }; function factorial(n: number): BigInt { let result = BigInt(1); for (let i = 2; i <= n; i++) { result *= BigInt(i); } return result; } export {};

🧑‍💻 FactorialCalculator.tsx – React component

import React, { useState, useRef } from "react"; const FactorialCalculator: React.FC = () => { const [input, setInput] = useState<number>(100000); const [result, setResult] = useState<number | null>(null); const [loading, setLoading] = useState(false); const [totalTime, setTotalTime] = useState(0); const workerRef = useRef<Worker | null>(null); const handleCalculate = () => { setResult(null); setTotalTime(0); setLoading(true); const startTime = performance.now(); // Start timing const worker = new Worker(new URL("@src/worker/factorialWorker", import.meta.url)); workerRef.current = worker; worker.postMessage(input); worker.onmessage = (e: MessageEvent<number>) => { const endTime = performance.now(); // End timing setTotalTime(endTime - startTime); // Calculate total time in milliseconds setResult(e.data); setLoading(false); worker.terminate(); }; }; return ( <div> <input type="number" value={input} onChange={(e) => setInput(Number(e.target.value))} placeholder="Enter a positive integer" /> <button onClick={handleCalculate} disabled={loading}> {loading ? "Calculating..." : "Calculate Factorial"} </button> {result !== null && <p>Result: {result}</p>} {totalTime > 0 && <p>Calculation time: {totalTime.toFixed(3)} ms</p>} </div> ); }; export default FactorialCalculator;

Result:

Without Web WorkerWith Web Worker
Without Web Worker
With Web Worker

As you can see, using a Web Worker results in a much smoother UI compared to not using one.

4. Notes When Using Web Workers

  • You cannot access the DOM from inside a worker file.
  • Make sure postMessage communication is clear and avoids race conditions.
  • You can use Comlink to simplify communication.

5. When Should You Use Web Workers?

ScenarioShould You Use a Worker?
Heavy computation like encryption, file compression✅ Yes
Processing large JSON files (>1MB)✅ Yes
Simple UI updates❌ No
Fetch API, server calls❌ No (just use async/await 😄)

6. Other Advanced Options

  • SharedWorker: share a worker across multiple tabs.
  • Service Worker: handles requests, caching – different from Web Worker.
  • OffscreenCanvas: render canvas inside a worker (great for graphics).

Conclusion

Web Workers are a powerful tool to improve performance in modern web applications. Offloading heavy tasks from the main thread helps keep the UI smooth and user-friendly. Consider using Web Workers when you notice your browser lagging during complex operations.

Tags

Buy Me A Coffee
    PerformanceJavaScript