什么是Web Worker ?
当HTML页面中执行JS时,页面的状态是不可响应的,直到脚本已完成。
Web Worker是运行在后台的JavaScript,独立于其它脚本,不会影响页面的性能。您可以继续继续做任何想做的事情:点击、选取内容等,而此时web worker在后台运行。
在学习Web Worker前,我们先举个例子看看执行复杂的JS时,是不是会影响页面性能。
封装一个函数实现斐波那契数列
:数学公式:F(n)=F(n-1)+F(n-1);即当前值等于前两个值之和。
代码示例:
<!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>
</head>
<body>
<input type="text" id="number" />
<button id="button">计算</button>
<script>
function fibonacci(n) {
return n <= 2 ? 1 : fibonacci(n - 1) + fibonacci(n - 2)
}
var button = document.querySelector('#button')
button.onclick = function () {
var input = document.querySelector('#number')
var result = fibonacci(input.value)
alert(result)
}
</script>
</body>
</html>
上面是gif格式的动图,你会发现当点击‘计算’按钮后并不是立即弹出结果,而是有个短暂时间才弹出来。这是因为这是一段递归计算的代码,即函数不断进入栈中执行。由于JS是单线程的,而且UI界面的渲染交互都是主线程的工作,那么在fibonacci函数执行完前都占用了主线程,那么此时我们点击页面是无法进行任何交互的,这给用户很差的体验。
由于JS是单线程的,无法解决该问题,但HTML5提供了Web Worker是一个JavaScript多线程解决方案。
我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面。
下面开始使用web worker:
创建一个work.js文件,它将在分线程执行:
function fibonacci(n) {
return n <= 2 ? 1 : fibonacci(n - 1) + fibonacci(n - 2)
}
var onmessage = function (event) {
let value = event.data //从event中获取主线程传来的数据
let result = fibonacci(value)
postMessage(result) //向主线程返回计算结果
}
修改HTML的代码:
<!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>
</head>
<body>
<input type="text" id="number" />
<button id="button">计算</button>
<script>
var button = document.querySelector('#button')
button.onclick = function () {
let input = document.querySelector('#number')
let value = input.value
let worker = new Worker('./worker.js')//参数是work.js文件的路径
worker.postMessage(value) //向worker.js发送数据
worker.onmessage = function (event) {
alert(event.data)
console.log(event)
}
}
</script>
</body>
</html>
虽然使用web worker不会加快该函数的计算速度,但是主线程可以做其它事情。我们打印了event,看看属性:
event中的data属性就是返回的数据。web worker用法很简单,使用 postMessage
发送数据,onmessage
监听并通过event
接收数据。
主线程与分线程都有消息队列和EventLoop:

web worker虽好,但是也有一些缺点:
- 浏览器兼容性差
- worker内不能访问DOM,即不能更新UI
- 不能跨域加载JS
我们看看为什么worker.js内不能访问DOM?
在html文件中默认的全局对象是window
,所以我们能通过document.getXXX()等方法获取DOM元素;但是在worker.js文件中默认的全局对象,也就是this指针的指向不再是window,而是worker对象
,所以我们在worker.js中根本获取不到DOM,所以也就操作不了DOM更新不了UI。
在worker.js打印this指针:
正是worker.js中的this自动指向了worker对象,所以我们才能直接使用postMessage等方法。