[Lv3] Web Worker 應用:背景運算不阻塞 UI
Web Worker 是一個在瀏覽器背景執行緒中運行 JavaScript 的 API,讓你能夠執行耗時的運算而不會阻塞主執行緒(UI 執行緒)。
🎯 核心概念
問題背景
JavaScript 原本是單執行緒的,所有程式碼都在主執行緒執行:
// ❌ 耗時運算阻塞主執行緒
function heavyComputation() {
for (let i = 0; i < 10000000000; i++) {
// 複雜計算
}
return result;
}
// 執行時整個頁面凍結
const result = heavyComputation(); // UI 無法互動 😢
問題:
- 頁面卡住,使用者無法點擊、滾動
- 動畫停止
- 使用者體驗極差
Web Worker 解決方案
Web Worker 提供多執行緒能力,讓耗時任務在背景執行:
// ✅ 使用 Worker 在背景執行
const worker = new Worker('worker.js');
// 主執行緒不阻塞,頁面依然可以互動
worker.postMessage({ data: largeData });
worker.onmessage = (e) => {
console.log('背景運算完成:', e.data);
};
情境 1:大數據處理
// main.js
const worker = new Worker('worker.js');
// 處理大型 JSON 數據
worker.postMessage({ data: largeDataArray, action: 'process' });
worker.onmessage = function (e) {
console.log('處理結果:', e.data);
};
// worker.js
self.onmessage = function (e) {
const { data, action } = e.data;
if (action === 'process') {
// 執行耗時的數據處理
const result = data.map((item) => {
// 複雜運算
return heavyComputation(item);
});
self.postMessage(result);
}
};
情境 2:圖片處理
處理圖片濾鏡、壓縮、像素操作等,避免凍結 UI。
情境 3:複雜計算
數學運算(如質數計算、加密解密) 大型檔案的雜湊值計算 數據分析和統計
使用限制與注意事項
不能在 Worker 中做的事
- 直接操作 DOM
- 存取 window、document、parent 物件
- 使用某些 Web API(如 alert)
可以在 Worker 中使用
- XMLHttpRequest / Fetch API
- WebSocket
- IndexedDB
- 定時器(setTimeout、setInterval)
- 部分瀏覽器 API
// 不適合用 Worker 的情況
// 1. 簡單快速的運算(創建 Worker 本身有開銷)
const result = 1 + 1; // 不需要 Worker
// 2. 需要頻繁與主執行緒通訊
// 通訊本身有成本,可能抵消多執行緒優勢
// 適合用 Worker 的情況
// 1. 單次長時間運算
const result = calculatePrimes(1000000);
// 2. 批次處理大量數據
const processed = largeArray.map(complexOperation);
🎯 實際專案應用案例
案例:遊戲資料加密處理
在遊戲平台中,我們需要對敏感資料進行加密/解密:
// main.js - 主執行緒
const cryptoWorker = new Worker('/workers/crypto-worker.js');
// 加密玩家資料
function encryptPlayerData(data) {
return new Promise((resolve, reject) => {
cryptoWorker.postMessage({
action: 'encrypt',
data: data,
key: SECRET_KEY,
});
cryptoWorker.onmessage = (e) => {
if (e.data.success) {
resolve(e.data.encrypted);
} else {
reject(e.data.error);
}
};
});
}
// 使用
const encrypted = await encryptPlayerData(sensitiveData);
// 頁面不會卡頓,使用者可以繼續操作
// crypto-worker.js - Worker 執行緒
self.onmessage = function (e) {
const { action, data, key } = e.data;
try {
if (action === 'encrypt') {
// 耗時的加密運算
const encrypted = performHeavyEncryption(data, key);
self.postMessage({ success: true, encrypted });
}
} catch (error) {
self.postMessage({ success: false, error: error.message });
}
};
案例:大量遊戲資料篩選
// 在 3000+ 款遊戲中進行複雜篩選
const filterWorker = new Worker('/workers/game-filter.js');
// 篩選條件
const filters = {
provider: ['PG', 'PP', 'EVO'],
type: ['slot', 'live'],
minRTP: 96.5,
tags: ['popular', 'new'],
};
filterWorker.postMessage({
games: allGames, // 3000+ 款
filters: filters,
});
filterWorker.onmessage = (e) => {
displayGames(e.data.filtered); // 顯示篩選結果
};
// 主執行緒不卡頓,使用者可以繼續滾動、點擊
💡 面試重點
常見面試 問題
Q1: Web Worker 和主執行緒如何通訊?
A: 透過 postMessage 和 onmessage:
// 主執行緒 → Worker
worker.postMessage({ type: 'START', data: [1, 2, 3] });
// Worker → 主執行緒
self.postMessage({ type: 'RESULT', result: processedData });
// 注意:資料會被「結構化複製」(Structured Clone)
// 這意味著:
// ✅ 可以傳遞:Number, String, Object, Array, Date, RegExp
// ❌ 不能傳遞:Function, DOM 元素, Symbol
Q2: Web Worker 的效能開銷是什麼?
A: 主要有兩個開銷:
// 1. 創建 Worker 的開銷(約 30-50ms)
const worker = new Worker('worker.js'); // 需要載入檔案
// 2. 通訊的開銷(資料複製)
worker.postMessage(largeData); // 大資料複製耗時
// 解決方案:
// 1. 重用 Worker(不要每次都創建)
// 2. 使用 Transferable Objects(轉移所有權,不複製)
const buffer = new ArrayBuffer(1024 * 1024); // 1MB
worker.postMessage(buffer, [buffer]); // 轉移所有權
Q3: Transferable Objects 是什麼?
A: 轉移資料所有權,而不是複製:
// ❌ 一般做法:複製資料(慢)
const largeArray = new Uint8Array(10000000); // 10MB
worker.postMessage(largeArray); // 複製 10MB(耗時)
// ✅ Transferable:轉移所有權(快)
const buffer = largeArray.buffer;
worker.postMessage(buffer, [buffer]); // 轉移所有權(毫秒級)
// 注意:轉移後,主執行緒無法再使用該資料
console.log(largeArray.length); // 0(已轉移)
支援的 Transferable 類型:
ArrayBufferMessagePortImageBitmapOffscreenCanvas
Q4: 什麼時候應該使用 Web Worker?
A: 使用決策樹:
是否為耗時運算(> 50ms)?
├─ 否 → 不需要 Worker
└─ 是 → 繼續判斷
│
├─ 是否需要操作 DOM?
│ ├─ 是 → 不能用 Worker(考慮 requestIdleCallback)
│ └─ 否 → 繼續判斷
│
└─ 通訊頻率是否很高(> 60次/秒)?
├─ 是 → 可能不適合(通訊開銷大)
└─ 否 → ✅ 適合使用 Worker
適合的場景:
- ✅ 加密/解密
- ✅ 圖片處理(濾鏡、壓縮)
- ✅ 大資料排序/篩選
- ✅ 複雜數學運算
- ✅ 檔案解析(JSON、CSV)
不適合的場景:
- ❌ 簡單計算(開銷大於收益)
- ❌ 需要頻繁通訊
- ❌ 需要操作 DOM