单线程与多线程
在很长的一段时间内,JavaScript都是一门单线程语言,所以在JavaScript中任何会导致页面阻塞的操作都是异步运行的,虽然异步能最大限度的保证用户的浏览体验,但如果在同步代码中执行时间过长的话还是会导致页面假死
<!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 onclick="console.log('Hello World!')">log</button>
<script>
while (true) { }
</script>
</body>
</html>
可以看到页面没有任何东西加载出来,渲染主线程一直在执行JavaScript
虽然我们平时不会刻意的去写这种死循环,但毫无疑问的是,如果JavaScript中存在多线程的话能解决以前的很多问题
webworker
webworker是随着HTML5一起推出的一个API
Web Worker可以让Web应用程序具备后台的处理能力,它支持多线程处理功能,因此可以充分利用多核CPU带来的优势,将耗时长的任务分配给Web Worker运行,从而避免了有时页面反应迟缓,甚至假死的现象
我们使用new worker来创建一个新线程
const myWorker = new Worker("worker.js");
Worker构造器需要传入一个URI来指定js
self
和主线程不同, worker 运行在另一个全局上下文中,不同于当前的window,因此,在Worker内通过window获取全局作用域将返回错误,我们应该使用self来得到全局上下文
onmessage和postmessage
主线程与其他线程的通信统一使用onmessage和postmessage
以下是一个简单的通过Worker来实现计算器的案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Worker 加法计算器</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
.container {
text-align: center;
}
input {
margin: 5px;
}
</style>
</head>
<body>
<div class="container">
<h1>Web Worker 加法计算器</h1>
<input type="number" id="num1" placeholder="输入第一个数">
<input type="number" id="num2" placeholder="输入第二个数">
<button onclick="calculate()">计算</button>
<p>结果: <span id="result"></span></p>
</div>
<script>
if (window.Worker) {
const worker = new Worker('worker.js');
function calculate() {
const num1 = document.getElementById('num1').value;
const num2 = document.getElementById('num2').value;
worker.postMessage({ num1: parseFloat(num1), num2: parseFloat(num2) });
}
worker.onmessage = function (event) {
document.getElementById('result').textContent = event.data.result;
}
} else {
console.log('您的浏览器不支持Web Worker.');
}
</script>
</body>
</html>
Worker.js
self.onmessage = function (event) {
const { num1, num2 } = event.data;
const result = num1 + num2;
self.postMessage({ result });
}
webworker限制
虽然webworker让JavaScript从此拥有了多线程编程的能力,但webworker也有着诸多限制
- 不能跨域加载 JavaScript
- Worker 内代码不能访问 DOM
- 使用 Web Worker 加载数据没有 JSONP 和 Ajax 加载数据高效
线程安全
虽然Worker接口会生成真正的操作系统级别的线程,但webworker中我们无法去访问非线程安全的组件或者是DOM,哪怕是通过postmessage在主线程与worker之间传递的数据也是通过拷贝而不是共享来完成的,这说明传递给worker的数据本质上是原数据的副本,页面与worker不会共享同一个实例
shareWorker
webworker其实分为两种类型
- 专用线程
即我们上面提到的所有Worker,专用线程通过Worker来得到,只能由当前页面访问,在页面关闭时自动销毁 - 共享线程
共享线程通过SharedWorker来得到,可以被多个页面访问
和专用线程不同,共享线程必须要显式的打开端口并通过port对象来进行通信,但在专用线程中这一部分是隐式的