Web Worker API
Web Worker 使得在一个独立于 Web 应用程序主执行线程的后台线程中运行脚本操作成为可能。这样做的好处是可以在独立线程中执行费时的处理任务,使主线程(通常是 UI 线程)的运行不会被阻塞/放慢。
Web Worker概念与用法
Worker 是一个使用构造函数创建的对象(例如 Worker),它运行一个具名 JavaScript 文件——该文件包含将在 worker 线程中运行的代码。
除了标准的 JavaScript 函数集(如 String、Array、Object、JSON 等),你可以在 worker 线程中运行任何你喜欢的代码,有一些例外:你不能直接在 worker 线程中操作 DOM 元素,或使用 window对象中的某些方法和属性。
数据通过消息系统在 worker 和主线程之间发送——双方都使用 postMessage()
方法发送消息,并通过 onmessage
事件处理程序响应消息(消息包含在 message 事件的 data
属性中)。数据是复制的,而不是共享的。
Worker类型
有许多不同类型的 worker:
- 专用 worker 是由单个脚本使用的 worker。该上下文由 DedicatedWorkerGlobalScope
对象表示。 - Shared worker 是可以由在不同窗口、IFrame 等中运行的多个脚本使用的 worker ,只要它们与 worker 在同一域中。它们比专用的 worker 稍微复杂一点——脚本必须通过活动端口进行通信。
- Service Worker 基本上是作为代理服务器,位于 web 应用程序、浏览器和网络(如果可用)之间。它们的目的是(除开其他方面)创建有效的离线体验、拦截网络请求,以及根据网络是否可用采取合适的行动并更新驻留在服务器上的资源。它们还将允许访问推送通知和后台同步 API。
备注: 根据 web worker 规范,worker 错误事件不应该冒泡(参见 Firefox bug 1188141)。该规范已在 Firefox 42 中实现。
Worker全局上下文和函数
worker 在一个与当前 window不同的全局上下文中运行!虽然 Window 不能直接用于 worker,但许多相同的方法被定义在一个共享的混入(WindowOrWorkerGlobalScope
)中,并通过 worker 自己的 WorkerGlobalScope 衍生的上下文提供给它们:
- DedicatedWorker GlobalScope用于专用 worker
- SharedWorker GlobalScope (en-US) 用于共享 worker
- ServiceWorkerGlobalScope 用于 service worker
支持的WebAPI
备注: 如果列出的 API 被某一平台的特定版本所支持,那么一般可以认为它在 web worker 中是可用的。你也可以使用网站来测试对某个特定对象/函数的支持:Worker Playground
以下 Web API 对 worker 是可用的:
- Barcode Detection API (en-US)
- Broadcast Channel API
- Cache API
- Channel Messaging API
- Console API
- Web Crypto API (例如 Crypto)
- CSS Font Loading API
- CustomEvent
- Encoding API(例如TextEncoder、TextDecoder)
- Fetch API
- FileReader
- FileReaderSync(仅在worker中有效!)
- FormData
- ImageBitmap
- ImageData
- IndexedDB
- Media Source Extensions API (仅限专用 worker)
- Network Information API
- Notifications API
- OffscreenCanvas
- (和所有的canvascontextAPI)
...
worker 也可以派生其他 worker,所以这些 API 也是可用的:
- Worker
- WorkerGlobalScope
- WorkerLocation (en-US)
- WorkerNavigator (en-US)
Web Worker接口
Worker
表示正在运行的worker线程,允许你将信息传递到正在运行的worker程序代码。
WorkerLocation (en-US)
定义由Worker执行的脚本的绝对位置。
SharedWorker
表示一种特定的worker,可以从多个浏览上下文(即窗口、标签或iframe)甚至其他worker访
问。
WorkerGlobalScope
表示任意worker的通用作用域(对于正常的网页类容来说与Winddow有相同的作用)。不同类型
的worker都有从接口继承作用域对象,并且可以添加更多特定特定特性生。
DedicatedWorker GlobalScope
表示一个专用worker的作用域,继承自WorkerGlobalScope,且可添加一些特有的特性。
SharedWorker GlobalScope (en-US)
表示一个共享worker的作用域,继承自WorkerGlobalScope,且可添加一些特有的特性。
WorkerNavigator (en-US)
表示用户代理(客户端)的身份和状态。
示例
本次先讲一下 Worker的使用
代码结构
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width">
<title>Web Workers basic example</title>
<link rel="stylesheet" href="style.css">
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<h1>Web<br>Workers<br>basic<br>example</h1>
<div class="controls" tabindex="0">
<form>
<div>
<label for="number1">Multiply number 1: </label>
<input type="text" id="number1" value="0">
</div>
<div>
<label for="number2">Multiply number 2: </label>
<input type="text" id="number2" value="0">
</div>
</form>
<p class="result">Result: 0</p>
</div>
<script src="main.js"></script>
</body>
</html>
main.js
const first = document.querySelector('#number1');
const second = document.querySelector('#number2');
const result = document.querySelector('.result');
if (window.Worker) {
const myWorker = new Worker("worker.js");
first.onchange = function() {
myWorker.postMessage([first.value, second.value]);
console.log('Message posted to worker');
}
second.onchange = function() {
myWorker.postMessage([first.value, second.value]);
console.log('Message posted to worker');
}
myWorker.onmessage = function(e) {
result.textContent = e.data;
console.log('Message received from worker');
}
} else {
console.log('Your browser doesn\'t support web workers.');
}
worker.js
onmessage = function(e) {
console.log('Worker: Message received from main script');
const result = e.data[0] * e.data[1];
if (isNaN(result)) {
postMessage('Please write two numbers');
} else {
const workerResult = 'Result: ' + result;
console.log('Worker: Posting message back to main script');
postMessage(workerResult);
}
}
style.css
html {
background-color: #7D2663;
font-family: sans-serif;
}
h1 {
margin: 0;
font-size: 20vmin;
letter-spacing: -0.2rem;
position: absolute;
top: 0;
z-index: -1;
}
p {
margin: 0;
}
.controls {
padding: 4vw;
width: 75%;
margin: 10vw auto;
background-color: rgba(255,255,255,0.7);
border: 5px solid black;
opacity: 0.3;
transition: 1s all;
}
.controls:hover, .controls:focus {
opacity: 1;
}
.controls label, .controls p, .controls input {
font-size: 3vw;
}
.controls div {
padding-bottom: 1rem;
}
结果