Web Workers 是 Web 浏览器提供的一种可以在后台线程中运行脚本的技术,这样就可以避免阻塞用户界面(UI)。Web Workers 允许开发者执行长时间运行的任务,如复杂的计算、文件处理、网络请求等,而不影响用户的交互体验。
JavaScript 是一种单线程的语言,这意味着在任何给定的时间点,它只能执行一个任务。然而,这也意味着如果 JavaScript 需要执行一个耗时的任务,比如复杂的计算或者从服务器获取大量数据,那么这个任务会阻塞主线程。因此Web Workers 允许开发者在后台线程上运行 JavaScript 脚本,从而释放主线程来处理 UI 更新和其他交互任务。这有助于保持应用程序的流畅性和响应性。
由于Worker是可以调用其他的线程工作,我们便可以将大型的耗时性任务交给它来完成。
当我们使用JS线程执行类似的耗时任务时,对于页面的加载速度将会是灾难级的,我们前端的首要任务本来就是将页面加载速度尽可能提高,而数据的完整也是必要的,那么我们启用Worker将是十分必要的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="startCalculation">Start Calculation</button>
<p id="result"></p>
<script>
const button = document.getElementById('startCalculation');
const resultElement = document.getElementById('result');
button.addEventListener('click', () => {
let sum=0
for(let i=0 ;i<100000000;i++){
sum+=i
}
resultElement.textContent = 'Sum: ' + sum;
})
</script>
</body>
</html>
将这个计算内容放入Worker线程,再去重新调用:
self.onmessage = function (event) {
if (event.data.action === 'start') {
// 开始执行耗时任务
let sum = calculateSum();
// 将结果发送回主线程
self.postMessage(sum);
}
};
function calculateSum() {
let sum = 0;
for (let i = 1; i <= 100000000; i++) {
sum += i;
}
return sum;
}
这里我们解读一下这段代码:
button.addEventListener('click', () => {
// 创建一个新的 Worker
const worker = new Worker('./worker.js');
worker.onmessage = (event) => {
// 接收来自 Worker 的消息
setTimeout(() => {
resultElement.innerHTML = 'Sum: ' + event.data;
},1000);
};
// 向 Worker 发送开始信号
worker.postMessage({ action: 'start' });
});
Web Worker 通信流程
-
主线程向 Worker 发送消息:
- 主线程通过调用
worker.postMessage方法向 Worker 发送数据。
- 主线程通过调用
-
Worker 接收消息并执行任务:
- Worker 通过
self.onmessage事件处理器接收来自主线程的消息。 - 根据接收到的消息执行相应的任务。
- Worker 通过
-
Worker 向主线程发送结果:
- 当任务完成时,Worker 使用
self.postMessage方法向主线程发送结果。 - 主线程通过
worker.onmessage事件处理器接收这个结果。
- 当任务完成时,Worker 使用
-
主线程更新页面:
- 主线程接收到结果后,更新页面的内容以显示计算结果。
因此当我们去连续调用worker.postMessage去处理数据时,会不间断的一直触发worker.onmessage的监听反馈。
通过 worker.postMessage 方法向 Worker 发送了两个消息:一个是 { action: 'start' },另一个是 { action: 'print' }。当我向 Worker 发送消息时,这些消息被放入一个队列中。worker.postMessage 方法是异步的,这意味着当你发送消息时,主线程不会等待 Worker 处理完消息后再继续执行。
先从上面的打印可以直接的反馈出Worker的事项顺序:
- Worker 首先处理
{ action: 'start' }消息,开始计算。 - 一旦计算完成,Worker 会发送结果给主线程。
- 此时
onmessage事件处理器被调用,更新页面上的结果。 - 接下来 Worker 处理
{ action: 'print' }消息,并可能执行其他操作(如打印结果到控制台)。
而示例中,onmessage 事件处理器内的逻辑之所以最后执行,是因为它取决于 Worker 处理完消息并将结果发送回主线程。一旦 Worker 完成计算并将结果发送给主线程,onmessage 事件处理器就会被调用,并执行更新页面内容和输出结果到控制台的逻辑。因此如果是耗时性不大的任务是可能会在 { action: 'start' }立即完成然后触发onmessage内的执行逻辑的。
除此之外:Web Worker 有以下几个使用注意点。
(1)同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
(2)DOM 限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。
(3)通信联系
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
(4)脚本限制
Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。
(5)文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。