Web Workers ใน JavaScript

Web Workers ใน JavaScript

บทความนี้อธิบายเกี่ยวกับ Web Workers ใน 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 ในภาษา JavaScript

Web Worker ใน JavaScript คือกลไกที่ช่วยให้โค้ดสามารถทำงานในเธรดที่แยกออกจากเธรดหลักได้ สิ่งนี้ช่วยให้สามารถทำการคำนวณที่ใช้ทรัพยากรมากและการดำเนินการที่ใช้เวลานานแบบอะซิงโครนัสได้ โดยไม่ทำให้เธรดหลักหยุดทำงาน การใช้ Web Worker จะช่วยเพิ่มความไวต่อการตอบสนองของส่วนติดต่อกับผู้ใช้และปรับปรุงประสิทธิภาพของแอปพลิเคชัน

การใช้งานพื้นฐานของ Web Worker

ลองมาดูการใช้งานพื้นฐานของ Web Worker กัน

Web Worker ใหม่จะถูกสร้างขึ้นโดยการระบุไฟล์ 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};
  • โค้ดนี้แสดงตัวอย่างกลไกพื้นฐานของการใช้ Web Worker เพื่อร้องขอการประมวลผลจากเธรดหลักและรับผลลัพธ์แบบอะซิงโครนัส โค้ดจะส่งข้อความ '10' ไปที่ Web Worker และได้รับ '20' เป็นผลลัพธ์กลับมา เธรดหลักและ Web Worker จะสื่อสารกันด้วยการส่งและรับข้อความเพื่อร้องขอและรับผลลัพธ์ของการดำเนินงาน

ฟังก์ชันพื้นฐาน

Web Worker มีคุณสมบัติพื้นฐานดังต่อไปนี้

  • postMessage และ onmessage ใช้สำหรับส่งและรับข้อความระหว่างเธรดหลักกับ Web Worker ส่งข้อมูลด้วย postMessage และรับข้อมูลด้วย onmessage
  • เธรดที่แยกอิสระ Web Worker ทำงานอย่างอิสระอย่างสมบูรณ์จากเธรดหลัก ดังนั้นจึงไม่สามารถเข้าถึงตัวแปรและฟังก์ชันที่กำหนดในเธรดหลักได้ ข้อมูลที่ต้องการต้องถูกส่งผ่าน postMessage
  • นำเข้า Script หากต้องการโหลดสคริปต์จากภายนอกภายใน Web Worker ให้ใช้ 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};

หรือคุณสามารถใช้คำสั่ง import ได้ตามตัวอย่างด้านล่างนี้

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};
  • โดยการระบุ type: 'module' ในอาร์กิวเมนต์ของ new Worker คุณสามารถใช้ worker เป็น ES module และใช้ไวยากรณ์ import ได้

ความแตกต่างระหว่าง Web Worker และฟังก์ชัน async

Web Worker

คุณสมบัติ
  • แยกเธรดออกจากกัน Web Worker ทำงานโค้ด JavaScript ในเธรดที่แยกจากกัน ทำให้สามารถจัดการกระบวนการคำนวณที่ใช้ทรัพยากรมากได้โดยไม่ขัดขวางเธรดหลัก (UI thread)
  • การประมวลผลพร้อมกัน การใช้ Web Worker ทำให้โปรแกรม JavaScript สามารถประมวลผลพร้อมกันในหลายๆ เธรดได้
  • การสื่อสารข้อความ Web Worker และเธรดหลักจะไม่แชร์ข้อมูลโดยตรง แต่จะแลกเปลี่ยนข้อความผ่าน postMessage และ onmessage
  • ไม่สามารถเข้าถึง UI ได้ Web Worker ไม่สามารถเข้าถึง DOM ได้ จึงไม่สามารถจัดการ UI ได้โดยตรง
กรณีการใช้งาน
  • เหมาะสมสำหรับกระบวนการคำนวณที่ใช้ทรัพยากรมาก เช่น การประมวลผลภาพ, การวิเคราะห์ข้อมูล และการเข้ารหัส
  • สามารถใช้งานได้เมื่อคุณไม่ต้องการขัดขวางเธรดหลัก

async

คุณสมบัติ
  • ทำให้การประมวลผลแบบอะซิงโครนัสง่ายขึ้น ฟังก์ชัน async เป็นไวยากรณ์ที่ช่วยให้การประมวลผลแบบอะซิงโครนัสอ่านง่ายขึ้น โดยภายในใช้ Promise
  • เธรดเดียว ฟังก์ชัน async ทั้งหมดจะรันอยู่บนเธรดหลัก ดังนั้น คุณจำเป็นต้องหาวิธีหลีกเลี่ยงการขัดขวาง UI thread
  • เหมาะกับการทำงาน I/O เหมาะสำหรับงานอะซิงโครนัส เช่น การเชื่อมต่อเครือข่ายหรืออ่านไฟล์ แต่ไม่เหมาะกับการประมวลผลหนัก
กรณีการใช้งาน
  • เหมาะสำหรับการติดต่อสื่อสารเครือข่าย เช่น การเรียกใช้ API
  • เหมาะสำหรับการอ่านและเขียนไฟล์โดยใช้ API ของเบราว์เซอร์หรือ Node.js

ความแตกต่างที่สำคัญ

คุณสมบัติ Web Worker async
สภาพแวดล้อมการทำงาน เธรดแยกต่างหาก เธรดหลัก
วัตถุประสงค์ การประมวลผลแบบขนาน, ลดภาระของการคำนวณหนัก คำอธิบายที่กระชับของการดำเนินการแบบอะซิงโครนัส
การเข้าถึง DOM ไม่สามารถทำได้ สามารถทำได้
วิธีการสื่อสาร การส่งข้อความ (เช่น postMessage) ไม่จำเป็น (จัดการผ่านการเรียกฟังก์ชันโดยตรงหรือ await)
กรณีการใช้งาน การคำนวณที่ใช้เวลานาน, การวิเคราะห์ข้อมูล การดำเนินการ I/O, การเรียก API

เมื่อใดที่ควรใช้แต่ละอัน

สำหรับงานที่ใช้ CPU หนัก คุณสามารถใช้ Web Worker เพื่อลดภาระบนเธรดหลักได้ ในทางกลับกัน สำหรับการสื่อสารเครือข่ายหรือการดำเนินการ I/O คุณสามารถทำให้โค้ดของคุณง่ายขึ้นด้วยการใช้ async/await

โดยการผสมผสานทั้งสองอย่างอย่างเหมาะสม คุณสามารถทำให้ได้การประมวลผลแบบอะซิงโครนัสที่มีประสิทธิภาพ

ตัวอย่างการใช้งาน Web Worker

ตัวอย่างการถ่ายโอนการคำนวณที่ใช้ทรัพยากรมาก

ถัดไปเป็นตัวอย่างของการถ่ายโอนการประมวลผลหนักๆ

 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
  • โค้ดนี้เป็นตัวอย่างของการใช้ Web Worker เพื่อแยกและดำเนินงานหนัก เช่น การคำนวณลำดับฟีโบนักชี ออกจากเธรดหลัก

ตัวอย่างของการระบุชื่อภารกิจ

ถัดไปเป็นตัวอย่างของการส่งและรับข้อความโดยการระบุชื่อภารกิจ

 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 });
  • โค้ดนี้เป็นตัวอย่างของการดำเนินการตามชื่อ 'task' ในข้อความ โดยที่ 'Web Worker' จะส่งผลลัพธ์หรือข้อผิดพลาดของการคำนวณกลับไปยังเธรดหลักพร้อมกับชื่อภารกิจ

หมายเหตุ

เมื่อใช้ Web Worker ควรพิจารณาเรื่องต่อไปนี้

  • ไม่สามารถเข้าถึง DOM ได้ Web Worker ไม่สามารถแก้ไข UI หรือเข้าถึง DOM ได้ การจัดการ DOM จะต้องดำเนินการในเธรดหลัก
  • มีภาระเพิ่มเติมในการสื่อสาร จะมีภาระเพิ่มเติมเมื่อมีการแลกเปลี่ยนข้อมูลระหว่างเธรดหลักและ Web Worker สิ่งนี้อาจส่งผลต่อประสิทธิภาพ โดยเฉพาะอย่างยิ่งเมื่อมีการแลกเปลี่ยนข้อมูลจำนวนมากบ่อยครั้ง
  • นโยบายเดียวกับต้นทาง (Same-Origin Policy) สคริปต์ของ Web Worker อยู่ภายใต้นโยบายเดียวกับต้นทาง (Same-Origin Policy) ไม่สามารถโหลดสคริปต์จากโดเมนที่แตกต่างกันได้

การใช้ Web Worker สามารถปรับปรุงประสิทธิภาพและการตอบสนองของแอปพลิเคชันของคุณได้ แต่สิ่งสำคัญคือต้องใช้ให้เหมาะสมโดยคำนึงถึงข้อจำกัด เช่น การไม่สามารถจัดการ DOM ได้

คุณสามารถติดตามบทความข้างต้นโดยใช้ Visual Studio Code บนช่อง YouTube ของเรา กรุณาตรวจสอบช่อง YouTube ด้วย

YouTube Video