HTML5 - Web Workers
HTML5 - Web Workers
Section titled “HTML5 - Web Workers”Web 浏览器中的 JavaScript 传统上运行在单个线程(通常称为“主线程”)中。该线程负责处理用户界面更新(渲染、响应点击、滚动)以及执行 JavaScript 代码。如果脚本执行计算密集型任务(例如复杂的计算、处理大量数据或密集的网络请求),它可能会阻塞主线程,导致用户界面变得无响应或“卡死”。
考虑这个简化示例:点击“重型任务”按钮模拟一个长时间运行的操作。在它运行时,点击“打招呼”按钮不会立即触发提示框,因为主线程正忙。
<!DOCTYPE html><html><head> <title>Blocking Example</title> <script> function performHeavyTask() { console.log('Starting heavy task...'); // Simulate a long-running calculation const end = Date.now() + 5000; // Run for 5 seconds while (Date.now() < end) { // Busy-wait loop } console.log('Heavy task completed.'); alert('Heavy task finished!'); }
function sayHello() { alert('Hello there!'); } </script></head><body> <button onclick="performHeavyTask()">Run Heavy Task (Blocks UI)</button> <button onclick="sayHello()">Say Hello</button></body></html>什么是 Web Workers?
Section titled “什么是 Web Workers?”Web Workers 提供了一种机制,可以在后台线程中运行 JavaScript 代码,与主执行线程分开。这允许执行长时间运行或计算开销大的任务,而不会阻塞主线程,从而保持用户界面的响应性。
Worker 非常适合执行复杂计算、数据处理、后台同步或管理大型数据集等任务。
Worker 有不同类型,包括专用 Worker(Dedicated Workers,最常见的类型,绑定到单个脚本上下文)、共享 Worker(Shared Workers,可被同源的多个脚本访问)和 Service Worker(Service Workers,充当代理服务器,管理网络请求并支持离线功能和推送通知 - 这是一个更高级的主题)。
重要限制: 在 Web Worker 中运行的脚本在单独的全局上下文(DedicatedWorkerGlobalScope、SharedWorkerGlobalScope 等)中操作。它们不直接访问 window 对象、DOM(文档对象模型)或主线程中可用的许多其他 API。Worker 和主线程之间的通信通过消息传递发生。
Web Workers 如何工作
Section titled “Web Workers 如何工作”- 创建 Worker: 在你的主脚本中,创建一个新的
Worker对象,传递包含 Worker 代码的 JavaScript 文件路径。
const myWorker = new Worker('worker.js');- 定义 Worker 逻辑: 创建
worker.js文件。该脚本将在后台线程中运行。它通常监听来自主线程的消息,并将结果发回消息。
// worker.js
// Listen for messages from the main threadself.onmessage = function(event) { console.log('Message received from main script:', event.data);
if (event.data.command === 'calculate') { // Perform the calculation (e.g., sum numbers) const result = event.data.numbers.reduce((a, b) => a + b, 0);
// Send the result back to the main thread self.postMessage({ type: 'result', payload: result }); }};
// You can also send messages proactively// self.postMessage('Worker initialized!');- 通信: 使用
postMessage()方法在主线程和 Worker 之间发送数据(字符串、对象、数组、ArrayBuffers - 任何可以结构化克隆的数据)。使用onmessage事件处理器监听消息。
// main.jsconst myWorker = new Worker('worker.js');
// Send data to the workerconst dataToSend = { command: 'calculate', numbers: [1, 2, 3, 4, 5] };myWorker.postMessage(dataToSend);
// Listen for messages from the workermyWorker.onmessage = function(event) { console.log('Message received from worker:', event.data); if (event.data.type === 'result') { alert(`Calculation result: ${event.data.payload}`); }};- 导入脚本(可选): 如果你的 Worker 逻辑分散在多个文件中,你可以在 Worker 脚本中使用
importScripts()函数加载它们。
importScripts('helper.js', 'anotherHelper.js');
// Now functions/variables from helper.js and anotherHelper.js are availableself.onmessage = function(event) { // ... use imported functions ...};示例:分载密集型任务
Section titled “示例:分载密集型任务”让我们使用 Web Worker 重写最初的阻塞示例。
index.html(主页面):
<!DOCTYPE html><html><head> <title>Web Worker Example</title> <script> let worker;
function startHeavyTaskWorker() { if (window.Worker) { // Check for browser support if (!worker) { worker = new Worker('heavy_task_worker.js');
worker.onmessage = function(event) { console.log('Message received from worker:', event.data); alert('Worker finished the heavy task!'); // Optionally terminate the worker if no longer needed // worker.terminate(); // worker = null; };
worker.onerror = function(error) { console.error('Error in worker:', error.message, error); alert('An error occurred in the worker.'); }; }
console.log('Sending task command to worker...'); worker.postMessage({ command: 'startTask' });
} else { alert('Your browser does not support Web Workers.'); } }
function sayHello() { alert('Hello there!'); } </script></head><body> <button onclick="startHeavyTaskWorker()">Run Heavy Task (Worker)</button> <button onclick="sayHello()">Say Hello</button></body></html>heavy_task_worker.js(Worker 脚本):
// heavy_task_worker.jsself.onmessage = function(event) { if (event.data.command === 'startTask') { console.log('Worker starting heavy task...');
// Simulate a long-running calculation const end = Date.now() + 5000; // Run for 5 seconds while (Date.now() < end) { // Busy-wait loop }
console.log('Worker heavy task completed.'); self.postMessage({ status: 'completed' }); }};现在,当你点击“运行重型任务 (Worker)”时,长时间运行的操作将在后台运行。你可以立即点击“打招呼”,提示框会立即出现,因为主线程没有被阻塞。
终止 Worker
Section titled “终止 Worker”Web Worker 会持续运行,直到被显式终止。在主线程中,你可以使用 Worker 对象的 terminate() 方法停止一个 Worker:
if (worker) { worker.terminate(); worker = null; // Clear the reference console.log('Worker terminated.');}Worker 也可以通过在其脚本中调用 self.close() 来终止自身。
一个已终止的 Worker 不能重新启动;如果需要,必须创建一个新的实例。
Worker 脚本中发生的错误(例如,语法错误、运行时异常)可以通过在主线程中为 Worker 对象附加一个 onerror 事件处理器来捕获。事件对象提供了错误消息、文件名和行号等信息。
// main.jsmyWorker.onerror = function(error) { console.error(`Error in worker: ${error.message} at ${error.filename}:${error.lineno}`); // Handle the error appropriately, e.g., notify the user};适当地处理错误,例如通知用户。
检查浏览器支持
Section titled “检查浏览器支持”你可以通过检查 window 对象上是否存在 Worker 对象来轻松检查浏览器是否支持 Web Workers:
if (window.Worker) { // Web Workers are supported console.log('Web Workers supported!'); // Proceed to create workers...} else { // Web Workers are not supported console.warn('Web Workers not supported in this browser.'); // Provide fallback or notify the user}