前言
再也没有比点击网页后毫无动静更令人心烦的事情了,但JavaScript采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,前面的任务没做完,后面的任务只能等着,这听起来也非常糟糕,如果再执行大量计算反而会使应用程序无响应,当时间过长时,浏览器甚至会显示警告并提示用户终止页面。
比如下面这样。
<!DOCTYPE HTML>
<html>
<head>
<script>
function bigLoop() {
for (var i = 0; i <= 1000000000; i += 1) {
var j = i;
}
alert("Completed " + j + "iterations" );
}
function sayHello(){
alert("Hello" );
}
</script>
</head>
<body>
<input type = "button" onclick = "bigLoop();" value = "循环" />
<input type = "button" onclick = "alert();" value = "alert" />
</body>
</html>
当点击按钮后,他会触发UI线程来创建两个任务并添加到队列,第一个任务是更新按钮的UI,用来指示按钮被点击,第二个任务是执行JavaScript,当所有UI线程的任务都执行完毕后,进程进入空闲状态,并等待新的任务加入列队,但是如果在任务运行期间和页面交互,不仅没有及时更新UI,还可能新的任务不会被添加的队列。
但Web Worker的出现改变了这一现状,他为应用程序带来了多线程。Web Worker允许长时间运行脚本,不会被其他用户交互的脚本中断,也最大限度地利用多个 CPU。
Web Worker创建
要想创建Web Worker,需要单独写一个JS文件,然后使用这个JS文件的URL进行初始化,里面包含Web Worker执行的代码,如果指定的文件存在,浏览器将产生一个新的工作线程,异步下载,直到文件完全下载并执行后,Worker才会启动。如果路径文件不存在,Worker将什么也不做。
worker.js
for (var i = 0; i <= 1000000000; i += 1) {
var j = i;
}
postMessage(j);
<!DOCTYPE HTML>
<html>
<head>
<script>
function calcu() {
var worker = new Worker('worker.js');
worker.onmessage = function (event) {
alert("Completed " + event.data + "iterations");
};
}
</script>
</head>
<body>
<input type="button" onclick="calcu();" value="计算" />
<button onclick="alert('hello')">hello</button>
</body>
</html>
Web Worker创建成功后,Web Worker与父页面之间的通信使用postMessage() 方法完成,Web Worker传递的消息通过主页中的onmessage事件获取,效果如下:
主线程向Web Workers传值
self.addEventListener('message', handleMessage);
function handleMessage(e) {
self.postMessage({ ...e.data, now_timer: Date.now() });
}
<!DOCTYPE HTML>
<html>
<head>
<script>
function calcu() {
const worker = new Worker('worker.js');
worker.addEventListener('message', handleMessage);
function handleMessage(e) {
console.log(e)
alert(JSON.stringify(e.data))
}
worker.postMessage({ name: "张三" });
}
</script>
</head>
<body>
<input type="button" onclick="calcu();" value="计算" />
<button onclick="alert('hello')">hello</button>
</body>
</html>
上面当Web Worker运行 worker.js后,Web Worker将在一个单独的线程中运行,监听消息,它接收来自主线程的消息,并向对象添加时间戳再原样返回,主应用程序还监听来自Web Worker的消息,然后输出。
需要注意以下几点:
-
消息传递必须是JSON可序列化的才能到达另一端,不能传递函数、对象引用等。
-
Web Worker无法访问
window对象,但他们可以访问self,这意味着无法与页面中的任何元素进行交互。 -
Web Worker不会自行停止,但启动它们的页面可以通过调用
terminate()方法停止它们,停止后的Web Worker不再响应消息。 -
Web Worker有自己的隔离状态,因为每个Worker都有自己的内存空间。例如,在主线程上声明的变量不能直接从工作线程中引用。
-
而在worker线程中,worker也可以调用自己的
close方法进行关闭:
浏览器限制
浏览器会限制了JavaScript任务的运行时间,此类限制分两种:调用栈大小限制和长时间运行脚本限制。