网站链接加标签,wordpress搜索加强,wordpress wpcontent,wordpress模板制作视频描述
由于 JavaScript 是单线程的#xff0c;当执行比较耗时的任务时#xff0c;就会阻塞主线程并导致页面无法响应#xff0c;这就是 Web Workers 发挥作用的地方。它允许在一个单独的线程#xff08;称为工作线程#xff09;中执行耗时的任务。这使得 JavaScript 代码可…描述
由于 JavaScript 是单线程的当执行比较耗时的任务时就会阻塞主线程并导致页面无法响应这就是 Web Workers 发挥作用的地方。它允许在一个单独的线程称为工作线程中执行耗时的任务。这使得 JavaScript 代码可以在后台执行而不会阻塞主线程并导致页面无响应。
Web Worker 是一个作为后台线程运行的脚本具有自己的引擎实例和事件循环。它与主执行线程并行运行并且不会阻塞事件循环。
主线程或工作线程本身可以启动任意数量的工作线程。生成 worker 脚本 主线程或另一个工作线程向新工作线程发送一条消息其中包含所有必要的数据。 工作线程中的事件处理程序执行并开始处理数据。 完成或失败时工作线程将一条带有计算结果的消息发送回主线程。 主线程中的事件处理程序执行、解析传入结果并运行必要的操作例如显示值。 重要性
Web Workers 为开发人员提供了在 Web 上实现多线程的方式这对于构建高性能的 Web 应用至关重要。通过将耗时的任务在后台独立于主线程中执行Web Workers 提高了网页的整体响应性并使用户体验更加流畅。以下是 Web Workers 在 Web 多线程中的重要性和好处
通过资源利用率
通过允许耗时任务在后台执行Web Workers 更有效地利用系统资源实现更快速和高效的数据处理并提高整体性能。这对于涉及大量数据处理或图像操作的 Web 应用尤为重要因为 Web Workers 可以在不影响用户界面的情况下执行这些任务。
增加稳定性和可靠性
通过将耗时任务隔离到单独的 worker 线程中Web Workers 帮助防止在主线程上执行大量代码时发生崩溃和错误。这使得开发人员更容易编写稳定可靠的 Web 应用减少用户的烦恼和数据丢失的可能性。
增强安全性
Web Workers 在与主线程分离的隔离环境中运行这有助于提高 Web 应用的安全性。此隔离防止恶意代码访问或修改主线程或其他 Web Workers 中的数据降低数据泄露或其他安全漏洞的风险。
更好的资源利用率
Web Workers 可以通过将耗时计算放到后台使主线程用于处理用户输入和其他任务来帮助提高资源利用率。这有助于提高系统的整体性能并减少崩溃或错误的可能性。此外通过利用多个 CPU 核心Web Workers 可以更有效地利用系统资源实现更快速和高效的数据处理。
Web Workers 还能够实现更好的负载平衡和扩展应用。通过允许任务在多个 worker 线程之间并行执行Web Workers 可以帮助将工作负荷均匀分配到多个核心或处理器上实现更快速和高效的数据处理。这对于经历高流量或需求的应用尤为重要因为 Web Workers 可以帮助确保应用可以处理增加的负载而不影响性能。
Web Workers 客户端使用
使用 JavaScript 创建 Web Worker 的步骤如下 创建一个新的 JavaScript 文件其中包含要在工作线程中运行的代码耗时任务。该文件不应包含对 DOM 的引用因为在工作线程中无法访问 DOM。 在主 JavaScript 文件中使用 Worker 构造函数创建一个新的worker对象。此构造函数接收一个参数即在步骤 1 中创建的 JavaScript 文件的 URL。
const worker new Worker(worker.js);向worker对象添加事件侦听器以处理主线程和工作线程之间发送的消息。onmessage 用于处理从工作线程发送来的消息postMessage 用于向工作线程发送消息。
worker.onmessage function(event) {console.log(Worker: event.data);
};worker.postMessage(Hello, worker!);在 Web Worker 的 JavaScript 文件中使用self对象的onmessage属性添加一个事件监听器来处理从主线程发出的消息。可以使用event.data属性访问发送的消息数据。
self.onmessage function(event) {console.log(Main: event.data);self.postMessage(Hello, Main!);
};接下来就运行应用并测试 Worker。可以在控制台看到以下信息表示主线程和 Worker 线程之间发送和接收了消息。
MainHello worker
WorkerHello Main我们可以使用terminate()函数来终止一个工作线程或者通过调用self上的close()函数使其自行终止。
// 从应用中终止一个工作线程
worker.terminate();
// 让一个工作线程自行终止
self.close();可以使用importScripts()函数将库或文件导入到工作线程中该函数可以接受多个文件。以下示例将script1.js和script2.js加载到工作线程 worker.js 中
importScripts(script1.js,script2);可以使用 onerror函数来处理工作线程抛出的错误
worker.onerror function(err) {console.log(遇到错误)
}Web Workers 服务端应用
服务器端 JavaScript 运行时也支持 Web Worker Node.js 在版本 10 中实现了类似 Web Worker 的功能称为 Worker thread工作进程。 Deno 复制了 Web Worker API因此语法与浏览器代码完全相同。它还提供了兼容模式可以填充 Node.js API以便可以使用该运行时的工作线程语法。 Bun 将支持浏览器和 Node.js 的 Web Worker API。
基本使用
要在 Node.js 中使用 Web Worker主脚本必须定义一个 Worker 对象其中包含相对于项目根目录的 Web Worker 脚本的名称。第二个参数定义了一个对象其中包含一个workerData属性该属性包含要发送的数据
const worker new Worker(./worker.js, {workerData: { a: 1, b: 2, c: 3 }
});与浏览器中的 Web Worker 不同 它在启动时无需运行worker.postMessage()。如果需要的话可以调用该方法并稍后发送更多数据它会触发parentPort.on(message)事件处理程序:
parentPort.on(message, e {console.log(e);
});一旦工作线程完成处理它会使用以下方法将结果数据发送回主线程
parentPort.postMessage(result);这将在在主脚本中触发 message 事件主线程接收到 worker 返回的结果
worker.on(message, result {console.log( result );
});在发送完消息后worker 就会终止。这也会触发一个exit事件如果希望运行清理或其他函数可以利用这个事件
worker.on(exit, code {
//...
});
除此之外还支持其他事件处理 messageerror当 worker 收到无法反序列化的数据时触发。 online当 worker 开始执行时触发。 error当 worker 脚本中发生 JavaScript 错误时触发。
在服务端一个单独的 Node.js 脚本文件可以同时包含主线程和工作线程的代码。脚本必须使用isMainThread检查自身是否在主线程上运行然后将自身作为工作线程进行调用可以在 ES 模块中使用import.meta.url作为文件引用或者在 CommonJS 中使用__filename。
import { Worker, isMainThread, workerData, parentPort } from node:worker_threads;if (isMainThread) {// 主线程const worker new Worker(import.meta.url, {workerData: { a: 1, b: 2, c: 3 }});worker.on(message, msg {});worker.on(exit, code {});
}
else {// 工作线程const result runSomeProcess( workerData );parentPort.postMessage(result);
}这种方式更快并且对于小型、自包含的单脚本项目来说是一个选择。如果是大型项目将 worker 脚本文件分开会更容易维护。
数据通信
主线程和工作线程之间的通信涉及到了数据序列化。可以使用表示固定长度原始二进制数据的SharedArrayBuffer对象在线程之间共享数据。以下是一个示例主线程定义了从 0 到 99 的 100 个数字元素并将其发送给工作线程
// main.js
import { Worker } from node:worker_threads;constbuffer new SharedArrayBuffer(100 * Int32Array.BYTES_PER_ELEMENT),value new Int32Array(buffer);value.forEach((v,i) value[i] i);const worker new Worker(./worker.js);worker.postMessage({ value });工作线程可以接收 value对象
// worker.js
import { parentPort } from node:worker_threads;parentPort.on(message, value {value[0] 100;
});主线程或工作线程都可以更改值数组中的元素数据将在两个线程之间保持一致。这可能会提高性能但有一些缺点 只能共享整数数据。 可能仍需要通知另一个线程更改。 存在两个线程可能同时更新同一值并且失去同步的风险。
Node.js 子进程
在 Node.js 中除了使用工作线程外还可以使用子进程来实现类似的功能。子进程用于启动其他应用、传递数据并接收结果。它们与工作线程类似但通常效率较低进程开销较大。
子进程和工作线程的选择取决于具体的应用场景。如果只需要在 Node.js 中执行其他任务或命令子进程是一种更好的选择。但如果需要在 Node.js 中进行复杂的计算或处理任务Web Worker 可能更适合。
Web Workers 应用场景
Web Workers 在实际应用中有许多常见且有用的应用场景。
处理 CPU 密集任务
假设有一个应用需要执行大量的 CPU 密集型计算。如果在主线程中执行这些计算用户界面可能会变得无响应用户体验将受到影响。为了避免这种情况可以使用 Web Worker 在后台执行这些计算。
在主线程中
// 创建一个新的 Web Worker
const worker new Worker(worker.js);// 定义一个函数来处理来自Web Worker的消息
worker.onmessage function(event) {const result event.data;console.log(result);
};// 向Web Worker发送一个消息以启动计算
worker.postMessage({ num: 1000000 });在 worker.js 中:
// 定义一个函数来执行计算
function compute(num) {let sum 0;for (let i 0; i num; i) {sum i;}return sum;
}// 定义一个函数来处理来自主线程的消息
onmessage function(event) {const num event.data.num;const result compute(num);postMessage(result);
};在这个例子中创建了一个新的 Web Worker并定义了一个函数来处理来自 Web Worker 的消息。然后向 Web Worker 发送一条消息并提供一个参数num指定要执行计算的迭代次数。Web Worker 接收到这条消息后在后台执行计算。当计算完成后Web Worker 向主线程发送一条包含结果的消息。主线程收到这个消息后将结果记录到控制台中。 在上面的例子中向 Web Worker 的compute()函数传递了数字 1000000。这意味着compute函数将需要将从 0 到一百万的所有数字相加。这涉及大量的额外操作可能需要很长时间才能完成特别是如果代码在较慢的计算机上运行或在浏览器标签中同时处理其他任务。
通过将这个任务分配给 Web Worker应用的主线程可以继续平稳运行而不会被计算密集型的任务阻塞。这使得用户界面保持响应并确保其他任务如用户输入或动画可以在没有延迟的情况下处理。
处理网络请求
假设有一个应用需要发起大量的网络请求。如果在主线程中执行这些请求可能会导致用户界面无响应用户体验差。为了避免这个问题可以利用 Web Worker 在后台处理这些请求。通过这样做主线程可以同时执行其他任务而 Web Worker 负责处理网络请求从而提高性能和改善用户体验。
在主线程中
// 创建一个新的 Web Worker
const worker new Worker(worker.js);// 定义一个函数来处理来自Web Worker的消息
worker.onmessage function(event) {const response event.data;console.log(response);
};// 向Web Worker发送一个消息以启动计算
worker.postMessage({ urls: [https://api.example.com/foo, https://api.example.com/bar] });在 worker.js 中:
// 定义一个函数来执行网络请求
function request(url) {return fetch(url).then(response response.json());
}// 定义一个函数来处理来自主线程的消息
onmessage async function(event) {const urls event.data.urls;const results await Promise.all(urls.map(request));postMessage(results);
};
在这个例子中创建一个新的 Web Worker 并定义一个函数来处理来自该 Worker 的消息。然后向 Worker 发送一个包含一组 URL 请求的消息。Worker 接收到这个消息后在后台使用 fetch API 执行请求。当所有请求完成后Worker 向主线程发送包含结果的消息。主线程接收到这个消息后将结果记录到控制台中。
并行处理
假设应用需要执行大量独立计算。 如果在主线程中依次执行这些计算用户界面将变得无响应用户体验将受到影响。 为了避免这种情况可以实例化多个 Web Worker 来并行执行计算。
在主线程中
// 创建三个新的 Web Worker
const worker1 new Worker(worker.js);
const worker2 new Worker(worker.js);
const worker3 new Worker(worker.js);// 定义三个处理来自 worker 的消息的函数
worker1.onmessage handleWorkerMessage;
worker2.onmessage handleWorkerMessage;
worker3.onmessage handleWorkerMessage;function handleWorkerMessage(event) {const result event.data;console.log(result);
}// 将任务分配给不同的 worker 对象并发送消息启动计算
worker1.postMessage({ num: 1000000 });
worker2.postMessage({ num: 2000000 });
worker3.postMessage({ num: 3000000 });在 worker.js 中:
// 定义一个函数来执行单个计算
function compute(num) {let sum 0;for (let i 0; i num; i) {sum i;
}return sum;
}// 定义一个函数来处理来自主线程的消息
onmessage function(event) {const result compute(event.data.num);postMessage(result);
};在这个例子中创建三个新的 Web Worker 并定义一个函数来处理来自该 Worker 的消息。然后向三个 Worker 分别发送一个要计算的数字消息。Worker 接收到这个消息后执行计算。当计算完成后Worker 向主线程发送包含结果的消息。主线程接收到这个消息后将结果记录到控制台中。
Web Workers 使用限制
Web Worker 是一个提高 Web 应用性能和响应能力的强大工具但它们也有一些限制和注意事项。
浏览器支持
目前所有主流浏览器、Node.js、Deno 和 Bun 都支持 Web Workers。 对 DOM 的访问受到限制
Web Worker 在单独的线程中运行无法直接访问主线程中的 DOM 或其他全局对象。这意味着不能直接从 Web Worker 中操作 DOM也不能访问像window或document这样的全局对象。
为了解决这个限制可以使用postMessage方法与主线程进行通信间接地更新 DOM 或访问全局对象。例如使用postMessage将数据发送到主线程然后根据接收到的消息来更新 DOM 或全局对象。
另外还有一些库可以帮助解决这个问题。例如WorkerDOM[1] 库允许在 Web Worker 中运行 DOM从而加快页面的渲染速度并提高性能。
现代桌面浏览器支持共享工作线程即在不同窗口、iframes 或工作线程中可被多个脚本访问的单个脚本它们通过独立的端口进行通信。但是大多数移动浏览器不支持共享工作线程所以对于大多数 Web 项目来说它们并不实用。
通信开销大
Web Worker 使用postMessage方法与主线程进行通信这可能会引入通信开销。通信开销指的是在两个或多个计算系统之间建立和维护通信所需的时间和资源量比如在 Web 应用中Web Worker 与主线程之间的通信。这可能导致消息处理延迟潜在地减慢应用程序的速度。为了最小化这种开销应该只在线程之间发送必要的数据避免发送大量数据或频繁发送消息。
调试工具有限
与在主线程中调试代码相比调试 Web Worker 可能更具挑战性因为可用的调试工具较少。为了简化调试过程可以使用控制台 API 在 Worker 线程中记录消息并使用浏览器开发者工具检查线程之间发送的消息。
代码复杂度
使用 Web Worker 可能会增加代码的复杂性因为需要管理线程之间的通信并确保数据正确传递。这可能会使编写、调试和维护代码更加困难因此应该仔细考虑是否有必要在应用中使用 Web Worker。
Web Workers 实践
上面提到了在使用 Web Workers 时可能会出现的一些潜在问题。下面就来看看如何缓解这些问题。
worker.on(message, result {console.log( result );
});消息批处理
消息批处理涉及将多个消息组合成一个批处理消息这比单独发送个别消息更有效。这种方法减少了主线程和 Web Worker 之间往返的数量它有助于最小化通信开销并提高应用的整体性能。
为了实现消息批量处理可以使用队列来累积消息并在队列达到一定阈值或经过设定时间后将消息批量发送。下面来在 Web Worker 中简单实现消息的批处理
// 创建一个消息队列累积消息
const messageQueue [];// 创建一个将消息添加到队列的函数
function addToQueue(message) {messageQueue.push(message);// 检查队列是否达到阈值大小if (messageQueue.length 10) {// 如果是请将批处理消息发送到主线程postMessage(messageQueue);// 清除消息队列messageQueue.length 0;}
}// 将消息添加到队列中
addToQueue({type: log, message: Hello, world!});// 再添加另一条消息到队列中
addToQueue({type: error, message: An error occurred.});在这个例子中 创建了一个消息队列用于累积需要发送到主线程的消息。每当使用addToQueue函数将消息添加到队列时检查队列是否已达到阈值大小10。如果是则使用postMessage方法将批处理消息发送到主线程。然后清除消息队列以准备进行下一次批处理。
通过以这种方式批处理消息可以减少主线程和 Web Worker 之间发送的消息总数从而提高应用性能。
避免同步方法
同步方法是阻塞其他代码执行的 JavaScript 函数或操作。同步方法可以阻塞主线程导致应变得无响应。为了避免这种情况应尽量避免在 Web Worker 中使用同步方法。而应该使用setTimeout()或setInterval()等异步方法来执行长时间运行的计算。
// 在Web Worker中
self.addEventListener(message, (event) {if (event.data.action start) {// 使用setTimeout来异步执行计算setTimeout(() {const result doSomeComputation(event.data.data);// 将结果发送回主线程self.postMessage({ action: result, data: result });}, 0);}
});注意内存使用情况
Web Workers 有自己的内存空间这个空间根据用户的设备和浏览器设置可能是有限的。为了避免内存问题应该注意你的 Web Worker 代码使用的内存量并避免不必要地创建大对象。例如
// 在Web Worker中
self.addEventListener(message, (event) {if (event.data.action start) {// 使用for循环处理一个数据数组const data event.data.data;const result [];for (let i 0; i data.length; i) {// 处理数组中的每个项并将结果添加到结果数组中const itemResult processItem(data[i]);result.push(itemResult);}// 将结果发送回主线程self.postMessage({ action: result, data: result });}
});在这段代码中Web Worker 处理一个数据数组并使用postMessage方法将结果发送回主线程。然而用于处理数据的 for 循环可能耗时较长。
导致这个问题的原因是代码一次性处理了整个数据数组这意味着所有的数据都必须同时加载到内存中。如果数据集非常大这可能导致 Web Worker 消耗大量的内存甚至超过浏览器为 Web Worker 分配的内存限制。
为了缓解这个问题可以考虑使用内置的 JavaScript 方法如forEach或reduce它们可以逐项处理数据避免一次性加载整个数组到内存中。
浏览器兼容性
大多数现代浏览器都支持 Web Worker但某些较旧的浏览器可能不支持它们。 为了确保与各种浏览器的兼容性应该在不同的浏览器和版本中测试 Web Worker 代码。 还可以使用功能检测来检查 Web Worker 是否受支持然后再在代码中使用它们如下所示
if (typeof Worker ! undefined) {const worker new Worker(worker.js);
} else {console.log(Web Workers are not supported in this browser.);
}
这段代码会检查当前浏览器是否支持 Web Workers并在支持时创建一个新的 Web Worker。如果 Web Workers 不受支持则该代码记录一条消息到控制台表示该浏览器不支持 Web Workers。
总结
Web Workers 是现代 Web 开发的一个基本特性它允许开发人员将 CPU 密集型任务放到单独的线程中执行从而提高应用的性能和响应能力。然而在处理 Web Workers 时需要记住一些重要的限制和注意事项例如无法访问 DOM 和数据类型之间传递的限制等。为了避免这些潜在问题可以采用上面提到的策略如使用异步方法并注意卸载的任务的复杂性。在未来使用 Web Workers 进行多线程似乎仍然是提高 Web 应用程序性能和响应能力的重要技术。