Web Workers trong JavaScript

Web Workers trong JavaScript

Bài viết này giải thích về Web Workers trong JavaScript.

YouTube Video

javascript-web-worker.html
  1<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4  <meta charset="UTF-8">
  5  <title>JavaScript &amp; HTML</title>
  6  <style>
  7    * {
  8        box-sizing: border-box;
  9    }
 10
 11    body {
 12        margin: 0;
 13        padding: 1em;
 14        padding-bottom: 10em;
 15        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 16        background-color: #f7f9fc;
 17        color: #333;
 18        line-height: 1.6;
 19    }
 20
 21    .container {
 22        max-width: 800px;
 23        margin: 0 auto;
 24        padding: 1em;
 25        background-color: #ffffff;
 26        border: 1px solid #ccc;
 27        border-radius: 10px;
 28        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
 29    }
 30
 31    .container-flex {
 32        display: flex;
 33        flex-wrap: wrap;
 34        gap: 2em;
 35        max-width: 1000px;
 36        margin: 0 auto;
 37        padding: 1em;
 38        background-color: #ffffff;
 39        border: 1px solid #ccc;
 40        border-radius: 10px;
 41        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
 42    }
 43
 44    .left-column, .right-column {
 45        flex: 1 1 200px;
 46        min-width: 200px;
 47    }
 48
 49    h1, h2 {
 50        font-size: 1.2rem;
 51        color: #007bff;
 52        margin-top: 0.5em;
 53        margin-bottom: 0.5em;
 54        border-left: 5px solid #007bff;
 55        padding-left: 0.6em;
 56        background-color: #e9f2ff;
 57    }
 58
 59    button {
 60        display: block;
 61        margin: 1em auto;
 62        padding: 0.75em 1.5em;
 63        font-size: 1rem;
 64        background-color: #007bff;
 65        color: white;
 66        border: none;
 67        border-radius: 6px;
 68        cursor: pointer;
 69        transition: background-color 0.3s ease;
 70    }
 71
 72    button:hover {
 73        background-color: #0056b3;
 74    }
 75
 76    #output {
 77        margin-top: 1em;
 78        background-color: #1e1e1e;
 79        color: #0f0;
 80        padding: 1em;
 81        border-radius: 8px;
 82        min-height: 200px;
 83        font-family: Consolas, monospace;
 84        font-size: 0.95rem;
 85        overflow-y: auto;
 86        white-space: pre-wrap;
 87    }
 88
 89    .highlight {
 90        outline: 3px solid #ffc107; /* yellow border */
 91        background-color: #fff8e1;  /* soft yellow background */
 92        transition: background-color 0.3s ease, outline 0.3s ease;
 93    }
 94
 95    .active {
 96        background-color: #28a745; /* green background */
 97        color: #fff;
 98        box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
 99        transition: background-color 0.3s ease, box-shadow 0.3s ease;
100    }
101  </style>
102</head>
103<body>
104    <div class="container">
105        <h1>JavaScript Console</h1>
106        <button id="executeBtn">Execute</button>
107        <div id="output"></div>
108    </div>
109
110    <script>
111        // Override console.log to display messages in the #output element
112        (function () {
113            // Override console.log
114            const originalLog = console.log;
115            console.log = function (...args) {
116                originalLog.apply(console, args);
117                const message = document.createElement('div');
118                message.textContent = args.map(String).join(' ');
119                output.appendChild(message);
120            };
121
122            // Override console.error
123            const originalError = console.error;
124            console.error = function (...args) {
125                originalError.apply(console, args);
126                const message = document.createElement('div');
127                message.textContent = args.map(String).join(' ');
128                message.style.color = 'red'; // Color error messages red
129                output.appendChild(message);
130            };
131        })();
132
133        document.getElementById('executeBtn').addEventListener('click', () => {
134            // Prevent multiple loads
135            if (document.getElementById('externalScript')) return;
136
137            const script = document.createElement('script');
138            script.src = 'javascript-web-worker.js';
139            script.id = 'externalScript';
140            //script.onload = () => console.log('javascript-web-worker.js loaded and executed.');
141            //script.onerror = () => console.log('Failed to load javascript-web-worker.js.');
142            document.body.appendChild(script);
143        });
144    </script>
145</body>
146</html>

Web Worker trong JavaScript

Web Worker trong JavaScript là một cơ chế cho phép mã chạy trên một luồng riêng biệt với luồng chính. Điều này cho phép thực hiện tính toán nặng và các tác vụ chạy lâu theo cách không đồng bộ, ngăn chặn luồng chính bị chặn. Bằng cách sử dụng Web Worker, khả năng phản hồi của giao diện người dùng được cải thiện và hiệu suất ứng dụng được nâng cao.

Cách sử dụng cơ bản của Web Worker

Hãy cùng tìm hiểu cách sử dụng cơ bản của Web Worker.

Một Web Worker mới được tạo ra bằng cách chỉ định một tập tin JavaScript.

1// worker.js (created as a separate file)
2onmessage = function(event) {
3    // Receive a message
4    const data = event.data;
5    // Perform heavy computation or processing
6    const result = data * 2;
7    // Return the result to the main thread
8    postMessage(result);
9};
 1// Main thread (main.js)
 2const worker = new Worker('worker.js');
 3
 4// Send a message to the worker
 5worker.postMessage(10);
 6
 7// Receive a message from the worker
 8worker.onmessage = function(event) {
 9    console.log('Result from Worker: ', event.data); // Result: 20
10};
11
12// Error handling
13worker.onerror = function(error) {
14    console.error('Worker error: ', error);
15};
  • Đoạn mã này minh họa cơ chế cơ bản của việc sử dụng Web Worker để yêu cầu tính toán từ luồng chính và nhận kết quả một cách bất đồng bộ. Nó gửi 10 như một thông điệp đến Web Worker và nhận về 20 như thông điệp kết quả. Luồng chính và Web Worker giao tiếp với nhau bằng cách gửi và nhận các thông điệp để yêu cầu và nhận kết quả xử lý.

Các chức năng cơ bản

Web Worker cung cấp các chức năng cơ bản sau đây.

  • postMessageonmessage Được sử dụng để gửi và nhận thông điệp giữa luồng chính và Web Worker. Gửi dữ liệu với postMessage và nhận dữ liệu với onmessage.
  • Sự độc lập của luồng Web Worker hoạt động hoàn toàn độc lập với luồng chính. Do đó, không thể truy cập các biến và hàm được định nghĩa trong luồng chính. Dữ liệu cần thiết cần được truyền qua postMessage.
  • Nhập khẩu tập lệnh Để nạp các tập lệnh bên ngoài trong Web Worker, hãy sử dụng importScripts().
1// mathUtils.js
2function double(n) {
3    return n * 2;
4}
1// worker.js
2importScripts('mathUtils.js');
3
4onmessage = function(event) {
5    const input = event.data;
6    const result = double(input); // mathUtils.jsの関数を使用
7    postMessage(result);
8};
 1// Main thread (main.js)
 2const worker = new Worker('worker.js');
 3
 4// Send a message to the worker
 5worker.postMessage(10);
 6
 7// Receive a message from the worker
 8worker.onmessage = function(event) {
 9    console.log('Result from Worker: ', event.data); // Result: 20
10};
11
12// Error handling
13worker.onerror = function(error) {
14    console.error('Worker error: ', error);
15};

Ngoài ra, bạn cũng có thể sử dụng import như được trình bày dưới đây.

1// lib.js
2
3// Simulated CPU-intensive task
4export function doHeavyWork(n) {
5    return n * 2;
6}
1// worker.js
2import { doHeavyWork } from './lib.js';
3
4self.onmessage = (event) => {
5  const data = event.data;
6  const result = doHeavyWork(data);
7  self.postMessage(result);
8};
 1// Main thread (main.js)
 2const worker = new Worker('worker.js', { type: 'module' });
 3
 4// Send a message to the worker
 5worker.postMessage(10);
 6
 7// Receive a message from the worker
 8worker.onmessage = function(event) {
 9    console.log('Result from Worker: ', event.data); // Result: 20
10};
11
12// Error handling
13worker.onerror = function(error) {
14    console.error('Worker error: ', error);
15};
  • Bằng cách chỉ định type: 'module' trong đối số của new Worker, bạn có thể xử lý worker như một mô-đun ES và sử dụng cú pháp import.

Sự khác biệt giữa Web Worker và các hàm async

Web Worker

Tính năng
  • Cách ly luồng Web Worker thực hiện mã JavaScript trên một luồng riêng biệt. Điều này cho phép thực hiện xử lý tính toán nặng mà không làm chặn luồng chính (UI thread).
  • Xử lý đồng thời Sử dụng Web Worker cho phép chương trình JavaScript thực hiện xử lý đồng thời trên nhiều luồng.
  • Giao tiếp qua thông điệp Web Worker và luồng chính không chia sẻ dữ liệu trực tiếp mà trao đổi thông điệp bằng cách sử dụng postMessageonmessage.
  • Không thể truy cập giao diện người dùng Web Worker không thể truy cập vào DOM, vì vậy nó không thể thao tác trực tiếp với giao diện người dùng.
Trường hợp sử dụng
  • Nó phù hợp cho các quy trình tính toán nặng như xử lý hình ảnh, phân tích dữ liệu và mã hóa.
  • Nó có thể được sử dụng khi bạn không muốn chặn luồng chính.

async

Tính năng
  • Đơn giản hóa xử lý bất đồng bộ Hàm async là cú pháp giúp xử lý bất đồng bộ dễ đọc hơn, bên trong sử dụng Promise.
  • Luồng đơn Tất cả các hàm async đều chạy trên luồng chính. Do đó, bạn cần tìm cách tránh chặn luồng UI.
  • Tối ưu cho các thao tác I/O Nó phù hợp cho các tác vụ bất đồng bộ như giao tiếp mạng và đọc dữ liệu từ tập tin, nhưng không phù hợp cho các phép tính nặng.
Trường hợp sử dụng
  • Nó phù hợp cho các tác vụ giao tiếp mạng như gọi API.
  • Nó phù hợp cho việc đọc và ghi tập tin bằng API của trình duyệt hoặc Node.js.

Sự Khác Biệt Chính

Tính năng Web Worker async
Môi Trường Thực Thi Luồng riêng Luồng chính
Mục Đích Xử lý song song, giảm tải các phép tính nặng Mô tả đơn giản về các hoạt động bất đồng bộ
Truy Cập DOM Không khả dụng Khả dụng
Phương Thức Giao Tiếp Truyền thông qua tin nhắn (ví dụ, postMessage) Không bắt buộc (xử lý qua các cuộc gọi hàm trực tiếp hoặc await)
Trường Hợp Sử Dụng Các phép tính tiêu tốn nhiều thời gian, phân tích dữ liệu Các thao tác I/O, cuộc gọi API

Khi Nào Sử Dụng Mỗi Loại

Đối với các tác vụ yêu cầu CPU cao, bạn có thể sử dụng Web Worker để giảm tải cho luồng chính. Mặt khác, đối với giao tiếp mạng hoặc các thao tác I/O, bạn có thể đơn giản hóa mã của mình bằng cách sử dụng async/await.

Bằng cách kết hợp cả hai một cách hợp lý, bạn có thể đạt được quá trình xử lý bất đồng bộ hiệu quả.

Ví dụ về Sử Dụng Web Worker

Ví dụ về chuyển tính toán nặng ra ngoài

Tiếp theo là một ví dụ về cách chuyển giao các phép tính nặng.

 1// worker.js
 2onmessage = function(event) {
 3    const num = event.data;
 4    const result = fibonacci(num);
 5    postMessage(result);
 6};
 7
 8function fibonacci(n) {
 9    if (n <= 1) return n;
10    return fibonacci(n - 1) + fibonacci(n - 2);
11}
1// Main thread
2const worker = new Worker('worker.js');
3
4worker.onmessage = function(event) {
5    console.log('Fibonacci result: ', event.data);
6};
7
8worker.postMessage(40); // Delegate heavy computation to the worker
  • Đoạn mã này là ví dụ về cách sử dụng Web Worker để tách và thực hiện các tác vụ nặng như tính dãy Fibonacci ra khỏi luồng chính.

Ví dụ về việc chỉ định tên tác vụ

Tiếp theo là ví dụ về gửi và nhận tin nhắn bằng cách chỉ định tên tác vụ.

 1// worker.js
 2self.onmessage = function(event) {
 3    switch (event.data.task) {
 4        case 'add':
 5            const result = event.data.a + event.data.b;
 6            self.postMessage({ task: 'result', value: result });
 7            break;
 8        default:
 9            self.postMessage({ task: 'error', message: 'Invalid Task' });
10    }
11};
 1// Main thread
 2const worker = new Worker('worker.js');
 3
 4worker.onmessage = function(event) {
 5    switch (event.data.task) {
 6        case 'result':
 7            console.log('Task result: ', event.data.value);
 8            break;
 9        case 'error':
10            console.error('Error: ', event.data.message);
11            break;
12    }
13};
14
15worker.postMessage({ task: 'add', a: 10, b: 20 });
  • Đoạn mã này là ví dụ về cách xử lý theo tên task trong tin nhắn, với Web Worker trả về kết quả tính toán hoặc lỗi về luồng chính cùng với tên tác vụ.

Ghi chú

Khi sử dụng Web Worker, hãy lưu ý các điểm sau.

  • Không thể truy cập DOM Web Worker không thể thao tác với giao diện người dùng hay truy cập DOM. Thao tác DOM cần được thực hiện trong luồng chính.
  • Chi phí giao tiếp Có một chút chi phí khi trao đổi dữ liệu giữa luồng chính và Web Worker. Điều này có thể ảnh hưởng đến hiệu suất, đặc biệt khi trao đổi lượng lớn dữ liệu thường xuyên.
  • Chính sách cùng nguồn gốc (Same-Origin Policy) Các tập lệnh Web Worker bị ràng buộc bởi chính sách cùng nguồn gốc (same-origin policy). Các tập lệnh không thể được tải từ các miền khác nhau.

Việc sử dụng Web Worker có thể cải thiện hiệu suất và tính phản hồi của ứng dụng của bạn, nhưng điều quan trọng là phải sử dụng nó một cách phù hợp với các giới hạn như không thể thao tác DOM.

Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.

YouTube Video