1. 10 Web 工作线程
    1. 10.1 简介
      1. 10.1.1 范围
      2. 10.1.2 示例
        1. 10.1.2.1 后台数字运算工作线程
        2. 10.1.2.2 使用 JavaScript 模块作为工作线程
        3. 10.1.2.3 共享工作线程简介
        4. 10.1.2.4 使用共享工作线程共享状态
        5. 10.1.2.5 委托
        6. 10.1.2.6 提供库
      3. 10.1.3 教程
        1. 10.1.3.1 创建专用工作线程
        2. 10.1.3.2 与专用工作线程通信
        3. 10.1.3.3 共享工作线程
    2. 10.2 基础设施
      1. 10.2.1 全局作用域
        1. 10.2.1.1 WorkerGlobalScope 通用接口
        2. 10.2.1.2 专用工作线程和 DedicatedWorkerGlobalScope 接口
        3. 10.2.1.3 共享工作线程和 SharedWorkerGlobalScope 接口
      2. 10.2.2 事件循环
      3. 10.2.3 工作线程的生命周期
      4. 10.2.4 处理模型
      5. 10.2.5 运行时脚本错误
      6. 10.2.6 创建工作线程
        1. 10.2.6.1 AbstractWorker 混合
        2. 10.2.6.2 工作线程的脚本设置
        3. 10.2.6.3 专用工作线程和 Worker 接口
        4. 10.2.6.4 共享工作线程和 SharedWorker 接口
      7. 10.2.7 并发硬件功能
    3. 10.3 可用于工作线程的 API
      1. 10.3.1 导入脚本和库
      2. 10.3.2 WorkerNavigator 接口
      3. 10.3.3 WorkerLocation 接口

10 Web 工作线程

Web_Workers_API

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome2+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+

Web_Workers_API/Using_web_workers

10.1 简介

10.1.1 范围

本节是非规范性的。

本规范定义了一个 API,用于在后台独立于任何用户界面脚本运行脚本。

这允许执行长时间运行的脚本,而不会被响应点击或其他用户交互的脚本中断,并允许执行长时间任务而不会让步以保持页面响应。

工作线程(此处将这些后台脚本称为工作线程)相对重量级,并且不打算大量使用。例如,为四百万像素图像的每个像素启动一个工作线程是不合适的。以下示例展示了一些工作线程的适当用法。

通常,工作线程预计是长期存在的,具有较高的启动性能成本和较高的每个实例内存成本。

10.1.2 示例

本节是非规范性的。

工作线程可以有多种用途。以下小节展示了这些用法的各种示例。

10.1.2.1 后台数字运算工作线程

本节是非规范性的。

工作线程最简单的用途是执行计算量大的任务,而不会中断用户界面。

在此示例中,主文档生成一个工作线程来(天真地)计算素数,并逐步显示最近找到的素数。

主页面如下

<!DOCTYPE HTML>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title>Worker example: One-core computation</title>
 </head>
 <body>
  <p>The highest prime number discovered so far is: <output id="result"></output></p>
  <script>
   var worker = new Worker('worker.js');
   worker.onmessage = function (event) {
     document.getElementById('result').textContent = event.data;
   };
  </script>
 </body>
</html>

Worker() 构造函数调用创建了一个工作线程并返回一个表示该工作线程的 Worker 对象,该对象用于与工作线程通信。该对象的 onmessage 事件处理程序允许代码接收来自工作线程的消息。

工作线程本身如下

var n = 1;
search: while (true) {
  n += 1;
  for (var i = 2; i <= Math.sqrt(n); i += 1)
    if (n % i == 0)
     continue search;
  // found a prime!
  postMessage(n);
}

此代码的大部分内容只是一个未经优化的素数搜索。 postMessage() 方法用于在找到素数时向页面发送消息。

在线查看此示例.

10.1.2.2 使用 JavaScript 模块作为工作线程

本节是非规范性的。

我们到目前为止的所有示例都展示了运行 经典脚本 的工作线程。工作线程可以使用 模块脚本 实例化,这具有以下好处:能够使用 JavaScript import 语句导入其他模块;默认情况下处于严格模式;以及顶级声明不会污染工作线程的全局作用域。

由于 import 语句可用,因此 importScripts() 方法将在模块工作线程内部自动失败。

在此示例中,主文档使用工作线程执行脱机线程图像处理。它从另一个模块导入使用的过滤器。

主页面如下

<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<title>Worker example: image decoding</title>

<p>
  <label>
    Type an image URL to decode
    <input type="url" id="image-url" list="image-list">
    <datalist id="image-list">
      <option value="https://html.whatwg.com.cn/images/drawImage.png">
      <option value="https://html.whatwg.com.cn/images/robots.jpeg">
      <option value="https://html.whatwg.com.cn/images/arcTo2.png">
    </datalist>
  </label>
</p>

<p>
  <label>
    Choose a filter to apply
    <select id="filter">
      <option value="none">none</option>
      <option value="grayscale">grayscale</option>
      <option value="brighten">brighten by 20%</option>
    </select>
  </label>
</p>

<div id="output"></div>

<script type="module">
  const worker = new Worker("worker.js", { type: "module" });
  worker.onmessage = receiveFromWorker;

  const url = document.querySelector("#image-url");
  const filter = document.querySelector("#filter");
  const output = document.querySelector("#output");

  url.oninput = updateImage;
  filter.oninput = sendToWorker;

  let imageData, context;

  function updateImage() {
    const img = new Image();
    img.src = url.value;

    img.onload = () => {
      const canvas = document.createElement("canvas");
      canvas.width = img.width;
      canvas.height = img.height;

      context = canvas.getContext("2d");
      context.drawImage(img, 0, 0);
      imageData = context.getImageData(0, 0, canvas.width, canvas.height);

      sendToWorker();
      output.replaceChildren(canvas);
    };
  }

  function sendToWorker() {
    worker.postMessage({ imageData, filter: filter.value });
  }

  function receiveFromWorker(e) {
    context.putImageData(e.data, 0, 0);
  }
</script>

然后工作线程文件为

import * as filters from "./filters.js";

self.onmessage = e => {
  const { imageData, filter } = e.data;
  filters[filter](imageData);
  self.postMessage(imageData, [imageData.data.buffer]);
};

它导入文件 filters.js

export function none() {}

export function grayscale({ data: d }) {
  for (let i = 0; i < d.length; i += 4) {
    const [r, g, b] = [d[i], d[i + 1], d[i + 2]];

    // CIE luminance for the RGB
    // The human eye is bad at seeing red and blue, so we de-emphasize them.
    d[i] = d[i + 1] = d[i + 2] = 0.2126 * r + 0.7152 * g + 0.0722 * b;
  }
};

export function brighten({ data: d }) {
  for (let i = 0; i < d.length; ++i) {
    d[i] *= 1.2;
  }
};

在线查看此示例.

10.1.2.3 共享工作线程简介

SharedWorker

所有当前引擎均支持。

Firefox29+Safari16+Chrome5+
Opera10.6+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android33+Safari iOS16+Chrome Android不支持WebView Android?Samsung Internet4.0–5.0Opera Android11–14

本节是非规范性的。

本节使用 Hello World 示例介绍共享工作线程。共享工作线程使用略微不同的 API,因为每个工作线程可以有多个连接。

此第一个示例展示了如何连接到工作线程以及工作线程如何在连接到页面时向页面发送消息。接收到的消息将显示在日志中。

这是 HTML 页面

<!DOCTYPE HTML>
<html lang="en">
<meta charset="utf-8">
<title>Shared workers: demo 1</title>
<pre id="log">Log:</pre>
<script>
  var worker = new SharedWorker('test.js');
  var log = document.getElementById('log');
  worker.port.onmessage = function(e) { // note: not worker.onmessage!
    log.textContent += '\n' + e.data;
  }
</script>

这是 JavaScript 工作线程

onconnect = function(e) {
  var port = e.ports[0];
  port.postMessage('Hello World!');
}

在线查看此示例.


此第二个示例通过更改两件事扩展了第一个示例:首先,使用 addEventListener() 而不是 事件处理程序 IDL 属性 接收消息,其次,向工作线程发送消息,导致工作线程发送另一条消息作为回复。接收到的消息再次显示在日志中。

这是 HTML 页面

<!DOCTYPE HTML>
<html lang="en">
<meta charset="utf-8">
<title>Shared workers: demo 2</title>
<pre id="log">Log:</pre>
<script>
  var worker = new SharedWorker('test.js');
  var log = document.getElementById('log');
  worker.port.addEventListener('message', function(e) {
    log.textContent += '\n' + e.data;
  }, false);
  worker.port.start(); // note: need this when using addEventListener
  worker.port.postMessage('ping');
</script>

这是 JavaScript 工作线程

onconnect = function(e) {
  var port = e.ports[0];
  port.postMessage('Hello World!');
  port.onmessage = function(e) {
    port.postMessage('pong'); // not e.ports[0].postMessage!
    // e.target.postMessage('pong'); would work also
  }
}

在线查看此示例.


最后,该示例扩展以展示两个页面如何连接到同一个工作线程;在本例中,第二个页面仅仅位于第一个页面上的 iframe 中,但相同的原理也适用于完全独立的页面在独立的 顶级可遍历对象 中。

这是外部 HTML 页面

<!DOCTYPE HTML>
<html lang="en">
<meta charset="utf-8">
<title>Shared workers: demo 3</title>
<pre id="log">Log:</pre>
<script>
  var worker = new SharedWorker('test.js');
  var log = document.getElementById('log');
  worker.port.addEventListener('message', function(e) {
    log.textContent += '\n' + e.data;
  }, false);
  worker.port.start();
  worker.port.postMessage('ping');
</script>
<iframe src="inner.html"></iframe>

这是内部 HTML 页面

<!DOCTYPE HTML>
<html lang="en">
<meta charset="utf-8">
<title>Shared workers: demo 3 inner frame</title>
<pre id=log>Inner log:</pre>
<script>
  var worker = new SharedWorker('test.js');
  var log = document.getElementById('log');
  worker.port.onmessage = function(e) {
   log.textContent += '\n' + e.data;
  }
</script>

这是 JavaScript 工作线程

var count = 0;
onconnect = function(e) {
  count += 1;
  var port = e.ports[0];
  port.postMessage('Hello World! You are connection #' + count);
  port.onmessage = function(e) {
    port.postMessage('pong');
  }
}

在线查看此示例.

10.1.2.4 使用共享工作线程共享状态

本节是非规范性的。

在此示例中,可以打开多个窗口(查看器),这些窗口都查看同一张地图。所有窗口共享相同的地图信息,一个工作线程协调所有查看器。每个查看器可以独立移动,但如果它们在地图上设置任何数据,则所有查看器都会更新。

主页面并不有趣,它仅仅提供了一种打开查看器的方法

<!DOCTYPE HTML>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title>Workers example: Multiviewer</title>
  <script>
   function openViewer() {
     window.open('viewer.html');
   }
  </script>
 </head>
 <body>
  <p><button type=button onclick="openViewer()">Open a new
  viewer</button></p>
  <p>Each viewer opens in a new window. You can have as many viewers
  as you like, they all view the same data.</p>
 </body>
</html>

查看器更复杂

<!DOCTYPE HTML>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title>Workers example: Multiviewer viewer</title>
  <script>
   var worker = new SharedWorker('worker.js', 'core');

   // CONFIGURATION
   function configure(event) {
     if (event.data.substr(0, 4) != 'cfg ') return;
     var name = event.data.substr(4).split(' ', 1)[0];
     // update display to mention our name is name
     document.getElementsByTagName('h1')[0].textContent += ' ' + name;
     // no longer need this listener
     worker.port.removeEventListener('message', configure, false);
   }
   worker.port.addEventListener('message', configure, false);

   // MAP
   function paintMap(event) {
     if (event.data.substr(0, 4) != 'map ') return;
     var data = event.data.substr(4).split(',');
     // display tiles data[0] .. data[8]
     var canvas = document.getElementById('map');
     var context = canvas.getContext('2d');
     for (var y = 0; y < 3; y += 1) {
       for (var x = 0; x < 3; x += 1) {
         var tile = data[y * 3 + x];
         if (tile == '0')
           context.fillStyle = 'green';
         else
           context.fillStyle = 'maroon';
         context.fillRect(x * 50, y * 50, 50, 50);
       }
     }
   }
   worker.port.addEventListener('message', paintMap, false);

   // PUBLIC CHAT
   function updatePublicChat(event) {
     if (event.data.substr(0, 4) != 'txt ') return;
     var name = event.data.substr(4).split(' ', 1)[0];
     var message = event.data.substr(4 + name.length + 1);
     // display "<name> message" in public chat
     var public = document.getElementById('public');
     var p = document.createElement('p');
     var n = document.createElement('button');
     n.textContent = '<' + name + '> ';
     n.onclick = function () { worker.port.postMessage('msg ' + name); };
     p.appendChild(n);
     var m = document.createElement('span');
     m.textContent = message;
     p.appendChild(m);
     public.appendChild(p);
   }
   worker.port.addEventListener('message', updatePublicChat, false);

   // PRIVATE CHAT
   function startPrivateChat(event) {
     if (event.data.substr(0, 4) != 'msg ') return;
     var name = event.data.substr(4).split(' ', 1)[0];
     var port = event.ports[0];
     // display a private chat UI
     var ul = document.getElementById('private');
     var li = document.createElement('li');
     var h3 = document.createElement('h3');
     h3.textContent = 'Private chat with ' + name;
     li.appendChild(h3);
     var div = document.createElement('div');
     var addMessage = function(name, message) {
       var p = document.createElement('p');
       var n = document.createElement('strong');
       n.textContent = '<' + name + '> ';
       p.appendChild(n);
       var t = document.createElement('span');
       t.textContent = message;
       p.appendChild(t);
       div.appendChild(p);
     };
     port.onmessage = function (event) {
       addMessage(name, event.data);
     };
     li.appendChild(div);
     var form = document.createElement('form');
     var p = document.createElement('p');
     var input = document.createElement('input');
     input.size = 50;
     p.appendChild(input);
     p.appendChild(document.createTextNode(' '));
     var button = document.createElement('button');
     button.textContent = 'Post';
     p.appendChild(button);
     form.onsubmit = function () {
       port.postMessage(input.value);
       addMessage('me', input.value);
       input.value = '';
       return false;
     };
     form.appendChild(p);
     li.appendChild(form);
     ul.appendChild(li);
   }
   worker.port.addEventListener('message', startPrivateChat, false);

   worker.port.start();
  </script>
 </head>
 <body>
  <h1>Viewer</h1>
  <h2>Map</h2>
  <p><canvas id="map" height=150 width=150></canvas></p>
  <p>
   <button type=button onclick="worker.port.postMessage('mov left')">Left</button>
   <button type=button onclick="worker.port.postMessage('mov up')">Up</button>
   <button type=button onclick="worker.port.postMessage('mov down')">Down</button>
   <button type=button onclick="worker.port.postMessage('mov right')">Right</button>
   <button type=button onclick="worker.port.postMessage('set 0')">Set 0</button>
   <button type=button onclick="worker.port.postMessage('set 1')">Set 1</button>
  </p>
  <h2>Public Chat</h2>
  <div id="public"></div>
  <form onsubmit="worker.port.postMessage('txt ' + message.value); message.value = ''; return false;">
   <p>
    <input type="text" name="message" size="50">
    <button>Post</button>
   </p>
  </form>
  <h2>Private Chat</h2>
  <ul id="private"></ul>
 </body>
</html>

查看器编写方式有几个关键要点值得注意。

多个侦听器。代码此处不使用单个消息处理函数,而是附加多个事件侦听器,每个侦听器都执行快速检查以查看它是否与消息相关。在此示例中,这没有太大区别,但如果多个作者想要使用单个端口与工作线程通信进行协作,它将允许使用独立代码,而不是必须对单个事件处理函数进行所有更改。

以这种方式注册事件侦听器还可以让你在完成操作时注销特定侦听器,就像在此示例中的 configure() 方法中所做的那样。

最后,工作线程

var nextName = 0;
function getNextName() {
  // this could use more friendly names
  // but for now just return a number
  return nextName++;
}

var map = [
 [0, 0, 0, 0, 0, 0, 0],
 [1, 1, 0, 1, 0, 1, 1],
 [0, 1, 0, 1, 0, 0, 0],
 [0, 1, 0, 1, 0, 1, 1],
 [0, 0, 0, 1, 0, 0, 0],
 [1, 0, 0, 1, 1, 1, 1],
 [1, 1, 0, 1, 1, 0, 1],
];

function wrapX(x) {
  if (x < 0) return wrapX(x + map[0].length);
  if (x >= map[0].length) return wrapX(x - map[0].length);
  return x;
}

function wrapY(y) {
  if (y < 0) return wrapY(y + map.length);
  if (y >= map[0].length) return wrapY(y - map.length);
  return y;
}

function wrap(val, min, max) {
  if (val < min)
    return val + (max-min)+1;
  if (val > max)
    return val - (max-min)-1;
  return val;
}

function sendMapData(viewer) {
  var data = '';
  for (var y = viewer.y-1; y <= viewer.y+1; y += 1) {
    for (var x = viewer.x-1; x <= viewer.x+1; x += 1) {
      if (data != '')
        data += ',';
      data += map[wrap(y, 0, map[0].length-1)][wrap(x, 0, map.length-1)];
    }
  }
  viewer.port.postMessage('map ' + data);
}

var viewers = {};
onconnect = function (event) {
  var name = getNextName();
  event.ports[0]._data = { port: event.ports[0], name: name, x: 0, y: 0, };
  viewers[name] = event.ports[0]._data;
  event.ports[0].postMessage('cfg ' + name);
  event.ports[0].onmessage = getMessage;
  sendMapData(event.ports[0]._data);
};

function getMessage(event) {
  switch (event.data.substr(0, 4)) {
    case 'mov ':
      var direction = event.data.substr(4);
      var dx = 0;
      var dy = 0;
      switch (direction) {
        case 'up': dy = -1; break;
        case 'down': dy = 1; break;
        case 'left': dx = -1; break;
        case 'right': dx = 1; break;
      }
      event.target._data.x = wrapX(event.target._data.x + dx);
      event.target._data.y = wrapY(event.target._data.y + dy);
      sendMapData(event.target._data);
      break;
    case 'set ':
      var value = event.data.substr(4);
      map[event.target._data.y][event.target._data.x] = value;
      for (var viewer in viewers)
        sendMapData(viewers[viewer]);
      break;
    case 'txt ':
      var name = event.target._data.name;
      var message = event.data.substr(4);
      for (var viewer in viewers)
        viewers[viewer].port.postMessage('txt ' + name + ' ' + message);
      break;
    case 'msg ':
      var party1 = event.target._data;
      var party2 = viewers[event.data.substr(4).split(' ', 1)[0]];
      if (party2) {
        var channel = new MessageChannel();
        party1.port.postMessage('msg ' + party2.name, [channel.port1]);
        party2.port.postMessage('msg ' + party1.name, [channel.port2]);
      }
      break;
  }
}

连接到多个页面。该脚本使用 onconnect 事件侦听器侦听多个连接。

直接通道。当工作线程从一个查看器接收“msg”消息并命名另一个查看器时,它会在两者之间建立直接连接,以便这两个查看器可以直接通信,而无需工作线程代理所有消息。

在线查看此示例.

10.1.2.5 委托

本节是非规范性的。

随着多核 CPU 变得越来越普遍,获得更好性能的一种方法是在多个工作线程之间分配计算量大的任务。在此示例中,要为从 1 到 10,000,000 的每个数字执行的计算量大的任务被分配给十个子工作线程。

主页面如下,它只是报告结果

<!DOCTYPE HTML>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title>Worker example: Multicore computation</title>
 </head>
 <body>
  <p>Result: <output id="result"></output></p>
  <script>
   var worker = new Worker('worker.js');
   worker.onmessage = function (event) {
     document.getElementById('result').textContent = event.data;
   };
  </script>
 </body>
</html>

工作线程本身如下

// settings
var num_workers = 10;
var items_per_worker = 1000000;

// start the workers
var result = 0;
var pending_workers = num_workers;
for (var i = 0; i < num_workers; i += 1) {
  var worker = new Worker('core.js');
  worker.postMessage(i * items_per_worker);
  worker.postMessage((i+1) * items_per_worker);
  worker.onmessage = storeResult;
}

// handle the results
function storeResult(event) {
  result += 1*event.data;
  pending_workers -= 1;
  if (pending_workers <= 0)
    postMessage(result); // finished!
}

它包含一个循环来启动子工作线程,然后是一个处理程序,等待所有子工作线程响应。

子工作线程的实现如下

var start;
onmessage = getStart;
function getStart(event) {
  start = 1*event.data;
  onmessage = getEnd;
}

var end;
function getEnd(event) {
  end = 1*event.data;
  onmessage = null;
  work();
}

function work() {
  var result = 0;
  for (var i = start; i < end; i += 1) {
    // perform some complex calculation here
    result += 1;
  }
  postMessage(result);
  close();
}

它们在两个事件中接收两个数字,执行为指定的数字范围执行的计算,然后将结果报告回父级。

在线查看此示例.

10.1.2.6 提供库

本节是非规范性的。

假设提供了一个加密库,它提供了三个任务

生成公钥/私钥对
获取端口,它将在该端口上发送两条消息,首先是公钥,然后是私钥。
给定明文和公钥,返回相应的密文
获取端口,可以向该端口发送任意数量的消息,第一个消息提供公钥,其余消息提供明文,每个消息都加密,然后在同一通道上作为密文发送。用户可以在完成内容加密后关闭端口。
给定密文和私钥,返回相应的明文
获取端口,可以向该端口发送任意数量的消息,第一个消息提供私钥,其余消息提供密文,每个消息都解密,然后在同一通道上作为明文发送。用户可以在完成内容解密后关闭端口。

库本身如下

function handleMessage(e) {
  if (e.data == "genkeys")
    genkeys(e.ports[0]);
  else if (e.data == "encrypt")
    encrypt(e.ports[0]);
  else if (e.data == "decrypt")
    decrypt(e.ports[0]);
}

function genkeys(p) {
  var keys = _generateKeyPair();
  p.postMessage(keys[0]);
  p.postMessage(keys[1]);
}

function encrypt(p) {
  var key, state = 0;
  p.onmessage = function (e) {
    if (state == 0) {
      key = e.data;
      state = 1;
    } else {
      p.postMessage(_encrypt(key, e.data));
    }
  };
}

function decrypt(p) {
  var key, state = 0;
  p.onmessage = function (e) {
    if (state == 0) {
      key = e.data;
      state = 1;
    } else {
      p.postMessage(_decrypt(key, e.data));
    }
  };
}

// support being used as a shared worker as well as a dedicated worker
if ('onmessage' in this) // dedicated worker
  onmessage = handleMessage;
else // shared worker
  onconnect = function (e) { e.port.onmessage = handleMessage; }


// the "crypto" functions:

function _generateKeyPair() {
  return [Math.random(), Math.random()];
}

function _encrypt(k, s) {
  return 'encrypted-' + k + ' ' + s;
}

function _decrypt(k, s) {
  return s.substr(s.indexOf(' ')+1);
}

请注意,此处的加密函数只是存根,不执行真正的加密。

此库可以使用如下方法

<!DOCTYPE HTML>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title>Worker example: Crypto library</title>
  <script>
   const cryptoLib = new Worker('libcrypto-v1.js'); // or could use 'libcrypto-v2.js'
   function startConversation(source, message) {
     const messageChannel = new MessageChannel();
     source.postMessage(message, [messageChannel.port2]);
     return messageChannel.port1;
   }
   function getKeys() {
     let state = 0;
     startConversation(cryptoLib, "genkeys").onmessage = function (e) {
       if (state === 0)
         document.getElementById('public').value = e.data;
       else if (state === 1)
         document.getElementById('private').value = e.data;
       state += 1;
     };
   }
   function enc() {
     const port = startConversation(cryptoLib, "encrypt");
     port.postMessage(document.getElementById('public').value);
     port.postMessage(document.getElementById('input').value);
     port.onmessage = function (e) {
       document.getElementById('input').value = e.data;
       port.close();
     };
   }
   function dec() {
     const port = startConversation(cryptoLib, "decrypt");
     port.postMessage(document.getElementById('private').value);
     port.postMessage(document.getElementById('input').value);
     port.onmessage = function (e) {
       document.getElementById('input').value = e.data;
       port.close();
     };
   }
  </script>
  <style>
   textarea { display: block; }
  </style>
 </head>
 <body onload="getKeys()">
  <fieldset>
   <legend>Keys</legend>
   <p><label>Public Key: <textarea id="public"></textarea></label></p>
   <p><label>Private Key: <textarea id="private"></textarea></label></p>
  </fieldset>
  <p><label>Input: <textarea id="input"></textarea></label></p>
  <p><button onclick="enc()">Encrypt</button> <button onclick="dec()">Decrypt</button></p>
 </body>
</html>

但是,API 的后续版本可能希望将所有加密工作卸载到子工作线程上。这可以通过以下方式完成

function handleMessage(e) {
  if (e.data == "genkeys")
    genkeys(e.ports[0]);
  else if (e.data == "encrypt")
    encrypt(e.ports[0]);
  else if (e.data == "decrypt")
    decrypt(e.ports[0]);
}

function genkeys(p) {
  var generator = new Worker('libcrypto-v2-generator.js');
  generator.postMessage('', [p]);
}

function encrypt(p) {
  p.onmessage = function (e) {
    var key = e.data;
    var encryptor = new Worker('libcrypto-v2-encryptor.js');
    encryptor.postMessage(key, [p]);
  };
}

function encrypt(p) {
  p.onmessage = function (e) {
    var key = e.data;
    var decryptor = new Worker('libcrypto-v2-decryptor.js');
    decryptor.postMessage(key, [p]);
  };
}

// support being used as a shared worker as well as a dedicated worker
if ('onmessage' in this) // dedicated worker
  onmessage = handleMessage;
else // shared worker
  onconnect = function (e) { e.ports[0].onmessage = handleMessage };

然后,小型子工作线程将如下所示。

用于生成密钥对

onmessage = function (e) {
  var k = _generateKeyPair();
  e.ports[0].postMessage(k[0]);
  e.ports[0].postMessage(k[1]);
  close();
}

function _generateKeyPair() {
  return [Math.random(), Math.random()];
}

用于加密

onmessage = function (e) {
  var key = e.data;
  e.ports[0].onmessage = function (e) {
    var s = e.data;
    postMessage(_encrypt(key, s));
  }
}

function _encrypt(k, s) {
  return 'encrypted-' + k + ' ' + s;
}

用于解密

onmessage = function (e) {
  var key = e.data;
  e.ports[0].onmessage = function (e) {
    var s = e.data;
    postMessage(_decrypt(key, s));
  }
}

function _decrypt(k, s) {
  return s.substr(s.indexOf(' ')+1);
}

请注意,API 的用户甚至不需要知道这一点正在发生——API 没有改变;库可以在不更改其 API 的情况下委托给子工作线程,即使它正在使用消息通道接受数据。

在线查看此示例.

10.1.3 教程

10.1.3.1 创建专用工作线程

本节是非规范性的。

创建工作线程需要一个指向 JavaScript 文件的 URL。 Worker() 构造函数只用指向该文件的 URL 作为其参数调用;然后创建一个工作线程并返回。

var worker = new Worker('helper.js');

如果希望工作线程脚本被解释为 模块脚本 而不是默认的 经典脚本,则需要使用稍微不同的签名。

var worker = new Worker('helper.mjs', { type: "module" });
10.1.3.2 与专用工作线程通信

本节是非规范性的。

专用工作线程在后台使用 MessagePort 对象,因此支持所有相同的功能,例如发送结构化数据、传输二进制数据和传输其他端口。

要接收来自专用工作线程的消息,请在 Worker 对象上使用 onmessage 事件处理程序 IDL 属性

worker.onmessage = function (event) { ... };

您还可以使用 addEventListener() 方法。

专用工作线程使用的隐式 MessagePort 在创建时其 端口消息队列 会隐式启用,因此 Worker 接口上没有等同于 MessagePort 接口的 start() 方法。

发送数据到工作线程,请使用 postMessage() 方法。结构化数据可以通过此通信通道发送。要有效地发送 ArrayBuffer 对象(通过传输而不是克隆它们),请在第二个参数的数组中列出它们。

worker.postMessage({
  operation: 'find-edges',
  input: buffer, // an ArrayBuffer object
  threshold: 0.6,
}, [buffer]);

要在工作线程内部接收消息,可以使用 onmessage 事件处理程序 IDL 属性

onmessage = function (event) { ... };

您还可以使用 addEventListener() 方法。

在这两种情况下,数据都提供在事件对象的 data 属性中。

要发送回复消息,您再次使用 postMessage()。它以相同的方式支持结构化数据。

postMessage(event.data.input, [event.data.input]); // transfer the buffer back
10.1.3.3 共享工作线程

SharedWorker

所有当前引擎均支持。

Firefox29+Safari16+Chrome5+
Opera10.6+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android33+Safari iOS16+Chrome Android不支持WebView Android?Samsung Internet4.0–5.0Opera Android11–14

本节是非规范性的。

共享工作线程由用于创建它的脚本的 URL 标识,可以选择使用显式名称。该名称允许启动特定共享工作线程的多个实例。

共享工作线程按 来源 范围限定。使用相同名称的两个不同站点不会发生冲突。但是,如果一个页面尝试使用与同一站点上的另一个页面相同的共享工作线程名称,但使用不同的脚本 URL,则会失败。

创建共享工作线程是使用 SharedWorker() 构造函数完成的。此构造函数将要使用的脚本的 URL 作为其第一个参数,并将工作线程的名称(如果有)作为第二个参数。

var worker = new SharedWorker('service.js');

与共享工作线程的通信是使用显式的 MessagePort 对象完成的。由 SharedWorker() 构造函数返回的对象在其 port 属性中保存对端口的引用。

worker.port.onmessage = function (event) { ... };
worker.port.postMessage('some message');
worker.port.postMessage({ foo: 'structured', bar: ['data', 'also', 'possible']});

在共享工作线程内部,使用 connect 事件宣布工作线程的新客户端。新客户端的端口由事件对象的 source 属性给出。

onconnect = function (event) {
  var newPort = event.source;
  // set up a listener
  newPort.onmessage = function (event) { ... };
  // send a message back to the port
  newPort.postMessage('ready!'); // can also send structured data, of course
};

10.2 基础设施

本标准定义了两种工作线程:专用工作线程和共享工作线程。专用工作线程一旦创建,就会与其创建者关联,但可以使用消息端口从专用工作线程与多个其他浏览上下文或工作线程进行通信。另一方面,共享工作线程是命名的,一旦创建,在同一 来源 中运行的任何脚本都可以获取对该工作线程的引用并与其通信。 服务工作线程 定义了第三种。 [SW]

10.2.1 全局范围

全局范围是工作线程的“内部”。

10.2.1.1 WorkerGlobalScope 通用接口

WorkerGlobalScope

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+
[Exposed=Worker]
interface WorkerGlobalScope : EventTarget {
  readonly attribute WorkerGlobalScope self;
  readonly attribute WorkerLocation location;
  readonly attribute WorkerNavigator navigator;
  undefined importScripts((TrustedScriptURL or USVString)... urls);

  attribute OnErrorEventHandler onerror;
  attribute EventHandler onlanguagechange;
  attribute EventHandler onoffline;
  attribute EventHandler ononline;
  attribute EventHandler onrejectionhandled;
  attribute EventHandler onunhandledrejection;
};

WorkerGlobalScope 充当特定类型工作线程全局范围对象的基类,包括 DedicatedWorkerGlobalScopeSharedWorkerGlobalScopeServiceWorkerGlobalScope

一个 WorkerGlobalScope 对象具有一个关联的 所有者集(一个 集合,包含 DocumentWorkerGlobalScope 对象)。它最初为空,并在创建或获取工作线程时填充。

它是一个 集合,而不是单个所有者,以适应 SharedWorkerGlobalScope 对象。

一个 WorkerGlobalScope 对象具有一个关联的 类型(“classic”或“module”)。它在创建期间设置。

一个 WorkerGlobalScope 对象具有一个关联的 url(null 或 URL)。它最初为 null。

一个 WorkerGlobalScope 对象具有一个关联的 名称(一个字符串)。它在创建期间设置。

WorkerGlobalScope 的每个子类,其 名称 具有不同的语义。对于 DedicatedWorkerGlobalScope 实例,它只是一个开发人员提供的名称,主要用于调试目的。对于 SharedWorkerGlobalScope 实例,它允许通过 SharedWorker() 构造函数获取对通用共享工作线程的引用。对于 ServiceWorkerGlobalScope 对象,它没有意义(因此根本不会通过 JavaScript API 公开)。

一个 WorkerGlobalScope 对象具有一个关联的 策略容器(一个 策略容器)。它最初是一个新的 策略容器

一个 WorkerGlobalScope 对象具有一个关联的 嵌入器策略(一个 嵌入器策略)。

一个 WorkerGlobalScope 对象具有一个关联的 模块映射。它是一个 模块映射,最初为空。

一个 WorkerGlobalScope 对象具有一个关联的 跨源隔离功能 布尔值。它最初为 false。

workerGlobal.self

WorkerGlobalScope/self

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera11.5+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android34+Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android?
返回 workerGlobal
workerGlobal.location

WorkerGlobalScope/location

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera11.5+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android?
返回 workerGlobalWorkerLocation 对象。
workerGlobal.navigator

WorkerGlobalScope/navigator

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera11.5+Edge79+
Edge (Legacy)17+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android?
返回 workerGlobalWorkerNavigator 对象。

workerGlobal.importScripts(...urls)

WorkerGlobalScope/importScripts

所有当前引擎均支持。

Firefox4+Safari4+Chrome4+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+
依次获取 urls 中的每个 URL,按照传递顺序逐一执行它们,然后返回(如果出现错误则抛出异常)。

self 属性必须返回 WorkerGlobalScope 对象本身。

location 属性必须返回 WorkerLocation 对象,其关联的 WorkerGlobalScope 对象WorkerGlobalScope 对象。

虽然 WorkerLocation 对象是在 WorkerGlobalScope 对象之后创建的,但这并不成问题,因为它无法从脚本中观察到。


以下是必须支持的 事件处理程序(及其对应的 事件处理程序事件类型),作为 事件处理程序 IDL 属性,由实现 WorkerGlobalScope 接口的对象。

事件处理程序 事件处理程序事件类型
onerror

WorkerGlobalScope/error_event

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera11.5+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android?
error
onlanguagechange

WorkerGlobalScope/languagechange_event

所有当前引擎均支持。

Firefox74+Safari4+Chrome4+
Opera11.5+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android?Safari iOS5+Chrome Android?WebView Android37+Samsung Internet?Opera Android?
languagechange
onoffline

WorkerGlobalScope/offline_event

Firefox29+Safari8+Chrome不支持
Opera?Edge不支持
Edge (旧版)?Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
offline
ononline

WorkerGlobalScope/online_event

Firefox29+Safari8+Chrome不支持
Opera?Edge不支持
Edge (旧版)?Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
online
onrejectionhandled rejectionhandled
onunhandledrejection unhandledrejection
10.2.1.2 专用工作线程和 DedicatedWorkerGlobalScope 接口

DedicatedWorkerGlobalScope

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+
[Global=(Worker,DedicatedWorker),Exposed=DedicatedWorker]
interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
  [Replaceable] readonly attribute DOMString name;

  undefined postMessage(any message, sequence<object> transfer);
  undefined postMessage(any message, optional StructuredSerializeOptions options = {});

  undefined close();

  attribute EventHandler onmessage;
  attribute EventHandler onmessageerror;
};

DedicatedWorkerGlobalScope 对象的行为就像它们有一个与之关联的隐式 MessagePort。此端口是工作线程创建时设置的通道的一部分,但未公开。此对象必须在 DedicatedWorkerGlobalScope 对象之前永远不会被垃圾回收。

该端口接收的所有消息都必须立即重新定位到 DedicatedWorkerGlobalScope 对象。

dedicatedWorkerGlobal.name

DedicatedWorkerGlobalScope/name

所有当前引擎均支持。

Firefox55+Safari12.1+Chrome70+
Opera?Edge79+
Edge (Legacy)18Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

返回 dedicatedWorkerGlobal名称,即传递给 Worker 构造函数的值。主要用于调试。

dedicatedWorkerGlobal.postMessage(message [, transfer ])

DedicatedWorkerGlobalScope/postMessage

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+
dedicatedWorkerGlobal.postMessage(message [, { transfer } ])

克隆 message 并将其传输到与 dedicatedWorkerGlobal 关联的 Worker 对象。 transfer 可以作为要传输而不是克隆的对象列表传递。

dedicatedWorkerGlobal.close()

DedicatedWorkerGlobalScope/close

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+

中止 dedicatedWorkerGlobal

name 获取器步骤是返回 this名称。它的值表示使用 Worker 构造函数赋予工作线程的名称,主要用于调试目的。

DedicatedWorkerGlobalScope 对象上的 postMessage(message, transfer)postMessage(message, options) 方法的行为就像在调用时,立即在端口上调用相应的 postMessage(message, transfer)postMessage(message, options),使用相同的参数,并返回相同的返回值。

关闭工作线程,给定一个 workerGlobal,请执行以下步骤

  1. 丢弃已添加到 workerGlobal相关代理事件循环任务队列 的任何 任务

  2. workerGlobalclosing 标志设置为 true。(这将阻止任何其他任务排队。)

DedicatedWorkerGlobalScope 对象上的 close() 方法步骤是 关闭工作线程,给定 this


以下是必须支持的 事件处理程序(及其对应的 事件处理程序事件类型),作为 事件处理程序 IDL 属性,由实现 DedicatedWorkerGlobalScope 接口的对象

事件处理程序 事件处理程序事件类型
onmessage

DedicatedWorkerGlobalScope/message_event

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android37+Samsung Internet?Opera Android11.5+
message
onmessageerror

DedicatedWorkerGlobalScope/messageerror_event

所有当前引擎均支持。

Firefox57+Safari16.4+Chrome60+
Opera?Edge79+
Edge (Legacy)18Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android47+
messageerror
10.2.1.3 共享工作线程和 SharedWorkerGlobalScope 接口

SharedWorkerGlobalScope

所有当前引擎均支持。

Firefox29+Safari16+Chrome4+
Opera10.6+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android?Safari iOS16+Chrome Android?WebView Android37+Samsung Internet?Opera Android11+
[Global=(Worker,SharedWorker),Exposed=SharedWorker]
interface SharedWorkerGlobalScope : WorkerGlobalScope {
  [Replaceable] readonly attribute DOMString name;

  undefined close();

  attribute EventHandler onconnect;
};

SharedWorkerGlobalScope 对象具有关联的 构造器来源构造器 URL凭据。它们在创建 SharedWorkerGlobalScope 对象时初始化,在 运行工作线程 算法中。

共享工作线程通过其 SharedWorkerGlobalScope 对象上的每个连接的 connect 事件接收消息端口。

sharedWorkerGlobal.name

SharedWorkerGlobalScope/name

所有当前引擎均支持。

Firefox29+Safari16+Chrome4+
Opera10.6+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android?Safari iOS16+Chrome Android?WebView Android?Samsung Internet?Opera Android11+

返回 sharedWorkerGlobal名称,即传递给 SharedWorker 构造函数的值。多个 SharedWorker 对象可以通过重用相同的名称对应于同一个共享工作线程(和 SharedWorkerGlobalScope)。

sharedWorkerGlobal.close()

SharedWorkerGlobalScope/close

所有当前引擎均支持。

Firefox29+Safari16+Chrome4+
Opera10.6+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android?Safari iOS16+Chrome Android?WebView Android?Samsung Internet?Opera Android11+

中止 sharedWorkerGlobal

name 获取器步骤为返回 thisname。其值表示可以使用 SharedWorker 构造函数获取对 worker 的引用的名称。

close() 方法步骤为给定 this 关闭 worker


以下是必须支持的 事件处理程序(及其对应的 事件处理程序事件类型),作为 事件处理程序 IDL 属性,由实现 SharedWorkerGlobalScope 接口的对象。

事件处理程序 事件处理程序事件类型
onconnect

SharedWorkerGlobalScope/connect_event

所有当前引擎均支持。

Firefox29+Safari16+Chrome4+
Opera10.6+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android?Safari iOS16+Chrome Android?WebView Android37+Samsung Internet?Opera Android11+
connect

10.2.2 事件循环

worker 事件循环任务队列仅具有事件、回调和网络活动作为 任务。这些 worker 事件循环运行 worker 算法创建。

每个 WorkerGlobalScope 对象都有一个 closing 标志,该标志最初必须为 false,但可以通过以下处理模型部分中的算法设置为 true。

一旦 WorkerGlobalScopeclosing 标志设置为 true,则 事件循环任务队列必须丢弃任何将添加到其中的进一步 任务(队列中已有的任务不受影响,除非另有说明)。实际上,一旦 closing 标志为 true,计时器停止触发,所有挂起的后台操作的通知都会被丢弃,等等。

10.2.3 worker 的生命周期

worker 通过 消息通道 及其 MessagePort 对象与其他 worker 和 Window 通信。

每个 WorkerGlobalScope 对象 worker 全局范围 都有一个 worker 的端口列表,该列表包含所有与另一个端口纠缠且只有一个(且仅一个)端口由 worker 全局范围 拥有的 MessagePort 对象。此列表包括在 专用 worker 的情况下隐式 MessagePort

在创建或获取 worker 时给定 环境设置对象 o要添加的相关所有者 取决于 o 指定的 全局对象 的类型。如果 o全局对象WorkerGlobalScope 对象(即,如果我们正在创建嵌套的专用 worker),则相关所有者是该全局对象。否则,o全局对象Window 对象,相关所有者是该 Window关联的 Document


如果 worker 的 WorkerGlobalScope所有者集为空

此定义的第二部分允许共享 worker 在页面加载时存活一段时间,以防该页面要再次联系共享 worker。用户代理可以使用它作为一种方法,以避免在用户在该站点内从一个页面导航到另一个页面时重新启动站点使用的共享 worker 的成本。

如果其任何 所有者 都是 完全活动Document 对象或 活动所需 worker,则称 worker 为 活动所需 worker

如果 worker 是 活动所需 worker 并且它具有未完成的计时器、数据库事务或网络连接,或者其 worker 的端口列表 不为空,或者其 WorkerGlobalScope 实际上是 SharedWorkerGlobalScope 对象(即,worker 是共享 worker),则称 worker 为 受保护的 worker

如果 worker 不是 活动所需 worker,但它是 允许的 worker,则称 worker 为 可挂起的 worker

10.2.4 处理模型

当用户代理要为具有 WorkerSharedWorker 对象 workerURL url环境设置对象 外部设置MessagePort 外部端口WorkerOptions 字典 选项 的脚本 运行 worker 时,它必须运行以下步骤。

  1. 如果 workerSharedWorker 对象,则令 is shared 为 true,否则为 false。

  2. owner 为给定 外部设置要添加的相关所有者

  3. unsafeWorkerCreationTime不安全的共享当前时间

  4. agent 为给定 外部设置is shared 获取专用/共享 worker 代理 的结果。在该代理中运行这些步骤的其余部分。

  5. 领域执行上下文 为给定 agent 和以下自定义项 创建新领域 的结果

  6. worker 全局范围领域执行上下文 的 Realm 组件的 全局对象

    这是上一步中创建的 DedicatedWorkerGlobalScopeSharedWorkerGlobalScope 对象。

  7. 使用 领域执行上下文外部设置unsafeWorkerCreationTime 设置 worker 环境设置对象,并令 内部设置 为结果。

  8. worker 全局范围name 设置为 选项name 成员的值。

  9. owner 附加worker 全局范围所有者集

  10. 如果 is shared 为 true,则

    1. worker 全局范围构造函数来源 设置为 外部设置来源

    2. worker 全局范围构造函数 URL 设置为 url

    3. worker 全局范围类型 设置为 选项type 成员的值。

    4. worker 全局范围凭据 设置为 选项credentials 成员的值。

  11. 如果 is shared 为 true,则令 destination 为“sharedworker”,否则为“worker”。

  12. 通过切换 选项type 成员的值来获取 script

    "classic"
    给定 url外部设置destination内部设置,并使用如下定义的 onCompleteperformFetch 获取经典 worker 脚本
    "模块"
    给定 url外部设置目标optionscredentials 成员的值、内部设置,并根据如下定义的 onCompleteperformFetch获取模块工作线程脚本图

    在这两种情况下,令 performFetch 为以下给定 requestisTopLevelprocessCustomFetchResponse执行获取钩子

    1. 如果 isTopLevel 为假,则使用 processResponseConsumeBody 设置为 processCustomFetchResponse 获取 request,并中止这些步骤。

    2. request保留客户端 设置为 内部设置
    3. 获取 request,其中 processResponseConsumeBody 设置为以下步骤,给定 响应 response 和 null、失败或 字节序列 bodyBytes

      1. 工作线程全局作用域url 设置为 responseurl

      2. 初始化工作线程全局作用域的策略容器,给定 工作线程全局作用域response内部设置

      3. 如果在 工作线程全局作用域 上执行 为全局对象运行 CSP 初始化 算法返回 "Blocked",则将 response 设置为 网络错误[CSP]

      4. 如果 工作线程全局作用域嵌入器策略跨源隔离兼容is shared 为真,则将 agent代理集群跨源隔离模式 设置为 "逻辑" 或 "具体"。选择哪一个由 实现定义

        这实际上应该在创建代理集群时设置,这需要重新设计本节。

      5. 如果使用 工作线程全局作用域外部设置response 检查全局对象的嵌入器策略 的结果为假,则将 response 设置为 网络错误

      6. 如果 agent代理集群跨源隔离模式 为 "具体",则将 工作线程全局作用域跨源隔离功能 设置为 true。

      7. 如果 is shared 为假且 owner跨源隔离功能 为假,则将 工作线程全局作用域跨源隔离功能 设置为 false。

      8. 如果 is shared 为假且 responseurl方案 为 "data",则将 工作线程全局作用域跨源隔离功能 设置为 false。

        这是一个保守的默认值,在我们弄清楚工作线程(通常)以及 data: URL 工作线程(特别是与它们的拥有者跨源)在权限策略上下文中将如何处理之前。有关更多详细信息,请参阅 w3c/webappsec-permissions-policy 问题 #207

      9. 使用 responsebodyBytes 运行 processCustomFetchResponse

    在这两种情况下,令给定 scriptonComplete 为以下步骤

    1. 如果 script 为 null 或 script要重新抛出的错误 不为 null,则

      1. 在给定 worker相关全局对象DOM 操作任务源 上排队一个全局任务,以 触发名为 error 的事件 在 worker 上。

      2. 内部设置 运行 环境丢弃步骤

      3. 中止这些步骤。

    2. worker工作线程全局作用域 关联。

    3. 内部端口内部设置领域 中的 新的 MessagePort 对象。

    4. 内部端口工作线程全局作用域 关联。

    5. 纠缠 外部端口内部端口

    6. 创建一个新的 WorkerLocation 对象并将其与 工作线程全局作用域 关联。

    7. 关闭孤立工作线程:监视工作线程,以便在它不再是 受保护的工作线程 且不晚于它不再是 允许的工作线程 之前,将 工作线程全局作用域closing 标志设置为 true。

    8. 挂起工作线程:监视工作线程,以便只要 工作线程全局作用域closing 标志为 false 且工作线程是 可挂起的工作线程,用户代理就会挂起该工作线程中脚本的执行,直到 closing 标志切换到 true 或工作线程不再是 可挂起的工作线程

    9. 设置 内部设置执行就绪标志

    10. 如果 script经典脚本,则 运行经典脚本 script。否则,它是一个 模块脚本运行模块脚本 script

      除了返回值或因异常而失败的通常可能性之外,这可能还会因下面定义的 终止工作线程 算法而被 过早中止

    11. 启用 外部端口端口消息队列

    12. 如果 is shared 为假,则启用工作线程的隐式端口的 端口消息队列

    13. 如果 is shared 为真,则在给定 工作线程全局作用域DOM 操作任务源排队一个全局任务,以 触发名为 connect 的事件 在 工作线程全局作用域 上,使用 MessageEvent,其中 data 属性初始化为空字符串,ports 属性初始化为包含 内部端口 的新 冻结数组,以及 source 属性初始化为 内部端口

    14. 启用关联的 服务工作线程客户端工作线程全局作用域相关设置对象ServiceWorkerContainer 对象的 客户端消息队列

    15. 事件循环:运行由 内部设置 指定的 负责的事件循环,直到它被销毁。

      事件循环 运行的 任务 处理事件或执行回调可能会因下面定义的 终止工作线程 算法而被 过早中止

      工作线程处理模型保持在此步骤,直到事件循环被销毁,事件循环在 closing 标志设置为 true 后发生,如 事件循环 处理模型中所述。

    16. 清除 工作线程全局作用域活动计时器映射

    17. 解开 工作线程的端口 列表中的所有端口。

    18. 清空 工作线程全局作用域拥有者集


当用户代理需要终止一个工作线程时,它必须并行于工作线程的主循环(上面定义的“运行一个工作线程”处理模型)执行以下步骤。

  1. 将工作线程的WorkerGlobalScope对象的closing标志设置为true。

  2. 如果在WorkerGlobalScope对象的相关代理事件循环任务队列中排队了任何任务,则丢弃它们而不进行处理。

  3. 中止当前在工作线程中运行的脚本。

  4. 如果工作线程的WorkerGlobalScope对象实际上是一个DedicatedWorkerGlobalScope对象(即工作线程是专用工作线程),则清空工作线程的隐式端口与其关联的端口的端口消息队列

当工作线程停止成为活动所需工作线程,并且即使在closing标志设置为true后工作线程仍继续执行时,用户代理可能会调用终止工作线程算法。

10.2.5 运行时脚本错误

每当工作线程的某个脚本中发生未捕获的运行时脚本错误时,如果该错误不是在处理先前的脚本错误期间发生的,则用户代理将为工作线程的WorkerGlobalScope对象报告该错误。

10.2.6 创建工作线程

10.2.6.1 AbstractWorker混合
interface mixin AbstractWorker {
  attribute EventHandler onerror;
};

以下是必须支持的事件处理程序(及其对应的事件处理程序事件类型),作为事件处理程序IDL属性,由实现AbstractWorker接口的对象提供。

事件处理程序 事件处理程序事件类型
onerror

ServiceWorker/error_event

所有当前引擎均支持。

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge(旧版)17+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

SharedWorker/error_event

所有当前引擎均支持。

Firefox29+Safari16+Chrome5+
Opera10.6+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android33+Safari iOS16+Chrome Android不支持WebView Android?Samsung Internet4.0–5.0Opera Android11–14

Worker/error_event

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+
error
10.2.6.2 工作线程的脚本设置

设置工作线程环境设置对象,给定一个JavaScript执行上下文execution context、一个环境设置对象outside settings和一个数字unsafeWorkerCreationTime

  1. inherited originoutside settings

  2. realmexecution context的Realm组件的值。

  3. worker global scoperealm全局对象

  4. settings object为一个新的环境设置对象,其算法定义如下。

    realm执行上下文

    返回execution context

    模块映射

    返回worker global scope模块映射

    API基本URL

    返回worker global scopeURL

    如果worker global scopeURL方案是“data”,则返回一个唯一的不透明源,否则返回inherited origin

    策略容器

    返回worker global scope策略容器

    跨源隔离功能

    返回worker global scope跨源隔离功能

    时间源

    返回使用worker global scope跨源隔离功能unsafeWorkerCreationTime进行粗化的结果。

  5. settings objectID设置为一个新的唯一的字符串,创建URL设置为worker global scopeURL顶级创建URL设置为null,目标浏览上下文设置为null,以及活动Service Worker设置为null。

  6. 如果worker global scope是一个DedicatedWorkerGlobalScope对象,则将settings object顶级源设置为outside settings顶级源

  7. 否则,将settings object顶级源设置为实现定义的值。

    有关正确定义此内容的最新信息,请参阅客户端存储分区

  8. realm的[[HostDefined]]字段设置为settings object

  9. 返回settings object

10.2.6.3 专用工作线程和Worker接口

Worker

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome2+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+
[Exposed=(Window,DedicatedWorker,SharedWorker)]
interface Worker : EventTarget {
  constructor((TrustedScriptURL or USVString) scriptURL, optional WorkerOptions options = {});

  undefined terminate();

  undefined postMessage(any message, sequence<object> transfer);
  undefined postMessage(any message, optional StructuredSerializeOptions options = {});
  attribute EventHandler onmessage;
  attribute EventHandler onmessageerror;
};

dictionary WorkerOptions {
  WorkerType type = "classic";
  RequestCredentials credentials = "same-origin"; // credentials is only used if type is "module"
  DOMString name = "";
};

enum WorkerType { "classic", "module" };

Worker includes AbstractWorker;
worker = new Worker(scriptURL [, options ])

Worker/Worker

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+

返回一个新的Worker对象。scriptURL将在后台获取并执行,创建一个新的全局环境,worker表示该环境的通信通道。options可用于通过name选项定义该全局环境的名称,主要用于调试目的。它还可以确保此新的全局环境支持JavaScript模块(指定type: "module"),如果指定了该选项,还可以用于通过credentials选项指定如何获取scriptURL

worker.terminate()

Worker/terminate

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome2+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+
中止worker关联的全局环境。
worker.postMessage(message [, transfer ])

Worker/postMessage

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome2+
Opera10.6+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+
worker.postMessage(message [, { transfer } ])

克隆message并将其传输到worker的全局环境。transfer可以作为要传输而不是克隆的对象列表传递。

当调用terminate()方法时,必须导致在与该对象关联的工作线程上运行终止工作线程算法。

Worker对象的行为就像它们有一个与之关联的隐式MessagePort。此端口是创建工作线程时设置的通道的一部分,但它不会公开。此对象在Worker对象之前绝不能被垃圾回收。

该端口接收的所有消息都必须立即重新定位到Worker对象。

Worker对象上的postMessage(message, transfer)postMessage(message, options)方法的行为就像,当调用它们时,它们立即调用端口上的相应postMessage(message, transfer)postMessage(message, options),并使用相同的参数,并返回相同的返回值。

postMessage()方法的第一个参数可以是结构化数据。

worker.postMessage({opcode: 'activate', device: 1938, parameters: [23, 102]});

以下是必须支持的事件处理程序(及其对应的事件处理程序事件类型),作为事件处理程序IDL属性,由实现Worker接口的对象提供。

事件处理程序 事件处理程序事件类型
onmessage message
onmessageerror messageerror

当调用Worker(scriptURL, options)构造函数时,用户代理必须运行以下步骤。

  1. compliantScriptURL为使用获取受信任类型兼容字符串算法,并传入TrustedScriptURLthis相关全局对象scriptURL、"Worker 构造函数"和"script"作为输入后得到的结果。

  2. 外部设置当前设置对象

  3. 工作者 URL为根据compliantScriptURL相对于外部设置进行URL 编码解析得到的结果。

    可以使用任何同源 URL(包括blob: URL)。也可以使用data: URL,但它们会创建一个具有不透明源的工作者。

  4. 如果工作者 URL为失败,则抛出一个"SyntaxError" DOMException

  5. 工作者为一个新的Worker对象。

  6. 外部端口外部设置领域中的一个新的MessagePort

  7. 外部端口工作者关联。

  8. 并行运行此步骤 并行

    1. 根据工作者工作者 URL外部设置外部端口选项 运行工作者

  9. 返回工作者

10.2.6.4 共享工作者和SharedWorker接口

SharedWorker

所有当前引擎均支持。

Firefox29+Safari16+Chrome5+
Opera10.6+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android33+Safari iOS16+Chrome Android不支持WebView Android?Samsung Internet4.0–5.0Opera Android11–14
[Exposed=Window]
interface SharedWorker : EventTarget {
  constructor((TrustedScriptURL or USVString) scriptURL, optional (DOMString or WorkerOptions) options = {});

  readonly attribute MessagePort port;
};
SharedWorker includes AbstractWorker;
sharedWorker = new SharedWorker(scriptURL [, name ])

SharedWorker/SharedWorker

所有当前引擎均支持。

Firefox29+Safari16+Chrome5+
Opera10.6+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android33+Safari iOS16+Chrome Android不支持WebView Android?Samsung Internet4.0–5.0Opera Android11–14

返回一个新的SharedWorker对象。scriptURL 将在后台获取并执行,创建一个新的全局环境,sharedWorker 代表与该环境的通信通道。name 可用于定义该全局环境的名称

sharedWorker = new SharedWorker(scriptURL [, options ])

返回一个新的SharedWorker对象。scriptURL 将在后台获取并执行,创建一个新的全局环境,sharedWorker 代表与该环境的通信通道。options 可用于通过 name 选项定义该全局环境的名称。它还可以确保此新的全局环境支持 JavaScript 模块(指定 type: "module"),如果指定了该模块,则还可以使用 credentials 选项指定如何获取scriptURL。请注意,尝试使用其 typecredentials 值与现有共享工作者不匹配的options构造共享工作者会导致返回的sharedWorker触发错误事件,并且不会连接到现有的共享工作者。

sharedWorker.port

SharedWorker/port

所有当前引擎均支持。

Firefox29+Safari16+Chrome5+
Opera10.6+Edge79+
Edge (旧版)?Internet Explorer不支持
Firefox Android33+Safari iOS16+Chrome Android不支持WebView Android?Samsung Internet4.0–5.0Opera Android11–14

返回sharedWorkerMessagePort对象,该对象可用于与全局环境通信。

port 属性必须返回其构造函数分配给它的值。它代表用于与共享工作者通信的MessagePort

用户代理有一个关联的共享工作者管理器,它是启动一个新的并行队列的结果。

为了简单起见,每个用户代理只有一个共享工作者管理器。实现可以为每个使用一个;这在观察上没有区别,并且可以实现更多的并发性。

SharedWorker(scriptURL, options)构造函数被调用时

  1. compliantScriptURL为使用获取受信任类型兼容字符串算法,并传入TrustedScriptURLthis相关全局对象scriptURL、"SharedWorker 构造函数"和"script"作为输入后得到的结果。

  2. 如果options是一个DOMString,则将options设置为一个新的WorkerOptions字典,其 name 成员设置为options的值,其他成员设置为其默认值。

  3. 外部设置当前设置对象

  4. urlRecord为根据compliantScriptURL相对于外部设置进行URL 编码解析得到的结果。

    可以使用任何同源 URL(包括blob: URL)。也可以使用data: URL,但它们会创建一个具有不透明源的工作者。

  5. 如果urlRecord为失败,则抛出一个"SyntaxError" DOMException

  6. 工作者为一个新的SharedWorker对象。

  7. 外部端口外部设置领域中的一个新的MessagePort

  8. 外部端口分配给工作者port属性。

  9. 如果外部设置安全上下文,则令调用者是否为安全上下文为 true;否则为 false。

  10. 外部存储密钥为根据外部设置运行获取用于非存储目的的存储密钥得到的结果。

  11. 将以下步骤入队共享工作者管理器

    1. 工作者全局范围为 null。

    2. 对于SharedWorkerGlobalScope对象列表中的每个范围迭代

      1. 工作者存储密钥为根据范围相关设置对象运行获取用于非存储目的的存储密钥得到的结果。

      2. 如果以下所有条件都为真

        1. 工作者全局范围设置为范围

        2. 中断.

      data: URL 会创建一个具有不透明源的工作者。会比较构造函数源构造函数 URL,以便可以在内使用相同的data: URL 获取相同的SharedWorkerGlobalScope对象,但不能用于绕过同源限制。

    3. 如果工作者全局范围不为 null,但用户代理已配置为不允许由工作者全局范围表示的工作者与脚本(其设置对象外部设置)进行通信,则将工作者全局范围设置为 null。

      例如,用户代理可以具有开发模式,该模式将特定顶级可遍历对象与所有其他页面隔离开,并且该开发模式中的脚本可能会被阻止连接到在普通浏览器模式下运行的共享工作者。

    4. 如果工作者全局范围不为 null,则检查工作者全局范围类型凭据是否与options的值匹配。如果不匹配,则将一个任务排队触发名为error的事件,并中止这些步骤。

    5. 如果工作者全局范围不为 null,则运行这些子子步骤

      1. 设置对象工作者全局范围相关设置对象

      2. 如果settings 对象处于安全上下文,则将workerIsSecureContext设置为true;否则设置为false。

      3. 如果workerIsSecureContext不等于callerIsSecureContext,则排队一个任务,在worker触发一个名为error的事件,并终止这些步骤。[SECURE-CONTEXTS]

      4. worker工作线程全局作用域 关联。

      5. 内部端口设置为settings 对象领域新的MessagePort

      6. 外部端口内部端口关联

      7. 使用DOM 操作任务源 排队一个任务,在工作线程全局范围触发一个名为connect的事件,使用MessageEvent,其中data属性初始化为空字符串,ports属性初始化为一个新的仅包含内部端口冻结数组source属性初始化为内部端口

      8. 将给定外部设置相关所有者添加到工作线程全局范围所有者集合中。

    6. 否则,并行地,给定workerurlRecord外部设置外部端口选项 运行一个工作线程

  12. 返回工作者

interface mixin NavigatorConcurrentHardware {
  readonly attribute unsigned long long hardwareConcurrency;
};
self.navigator.hardwareConcurrency

Navigator/hardwareConcurrency

Firefox48+Safari10.1–11Chrome37+
Opera?Edge79+
Edge (旧版)15+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

Navigator/hardwareConcurrency

Firefox48+Safari10.1–11Chrome37+
Opera?Edge79+
Edge (旧版)15+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

返回用户代理可能可用的逻辑处理器数量。

(这是一个跟踪向量。) navigator.hardwareConcurrency 属性的 getter 必须返回 1 到用户代理可能可用的逻辑处理器数量之间的数字。如果无法确定,则 getter 必须返回 1。

用户代理应倾向于公开可用的逻辑处理器数量,仅在存在用户代理特定限制(例如,创建工作线程数量的限制)或用户代理希望限制指纹识别可能性时使用较低的值。

10.3 工作线程可用的 API

10.3.1 导入脚本和库

importScripts(...urls) 方法的步骤如下

  1. urlStrings设置为« »。

  2. 对于urls中的每个url

    1. 将使用TrustedScriptURLthis相关全局对象url、"Worker importScripts"和"script"作为参数调用获取受信任类型兼容字符串算法的结果追加urlStrings中。

  3. 给定thisurlStrings 将脚本导入工作线程全局范围

要给定一个WorkerGlobalScope对象工作线程全局范围、一个urls列表(包含标量值字符串),以及一个可选的执行获取钩子performFetch 将脚本导入工作线程全局范围

  1. 如果工作线程全局范围类型为"module",则抛出TypeError异常。

  2. settings 对象设置为当前设置对象

  3. 如果urls为空,则返回。

  4. urlRecords设置为« »。

  5. 对于urls中的每个url

    1. urlRecord设置为给定url(相对于settings 对象编码解析URL的结果。

    2. 如果urlRecord为失败,则抛出一个"SyntaxError"DOMException

    3. 追加urlRecordurlRecords中。

  6. 对于urlRecords中的每个urlRecord

    1. 给定urlRecordsettings 对象 获取一个经典的工作线程导入脚本,如果提供了performFetch,则将其传递。如果成功,则将结果设置为script。否则,重新抛出异常。

    2. 使用rethrow errors参数设置为true 运行经典脚本script

      script将一直运行,直到它返回、解析失败、无法捕获异常或被上面定义的终止工作线程算法提前中止

      如果抛出异常或脚本提前中止,则终止所有这些步骤,让异常或中止继续由调用脚本处理。

《服务工作线程》是一个使用其自己的执行获取钩子运行此算法的规范示例。[SW]

10.3.2 WorkerNavigator 接口

WorkerNavigator

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox AndroidSafari iOS5+Chrome AndroidWebView AndroidSamsung InternetOpera Android12.1+

WorkerGlobalScope 接口的navigator 属性必须返回WorkerNavigator 接口的一个实例,该接口表示用户代理(客户端)的身份和状态。

[Exposed=Worker]
interface WorkerNavigator {};
WorkerNavigator includes NavigatorID;
WorkerNavigator includes NavigatorLanguage;
WorkerNavigator includes NavigatorOnLine;
WorkerNavigator includes NavigatorConcurrentHardware;

10.3.3 WorkerLocation 接口

WorkerLocation

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox AndroidSafari iOS5+Chrome AndroidWebView AndroidSamsung InternetOpera Android12.1+

WorkerLocation/toString

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera15+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox AndroidSafari iOS5+Chrome AndroidWebView Android37+Samsung InternetOpera Android14+
[Exposed=Worker]
interface WorkerLocation {
  stringifier readonly attribute USVString href;
  readonly attribute USVString origin;
  readonly attribute USVString protocol;
  readonly attribute USVString host;
  readonly attribute USVString hostname;
  readonly attribute USVString port;
  readonly attribute USVString pathname;
  readonly attribute USVString search;
  readonly attribute USVString hash;
};

一个WorkerLocation对象有一个关联的WorkerGlobalScope对象(一个WorkerGlobalScope对象)。

WorkerLocation/href

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox AndroidSafari iOS5+Chrome AndroidWebView AndroidSamsung InternetOpera Android12.1+

href getter 的步骤是返回thisWorkerGlobalScope对象url序列化后的结果。

WorkerLocation/origin

所有当前引擎均支持。

Firefox29+Safari10+Chrome38+
Opera?Edge79+
Edge (旧版)14+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

origin getter 的步骤是返回thisWorkerGlobalScope对象urlorigin序列化

WorkerLocation/protocol

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox AndroidSafari iOS5+Chrome AndroidWebView AndroidSamsung InternetOpera Android12.1+

protocol getter 的步骤是返回thisWorkerGlobalScope对象urlscheme,后跟":"。

WorkerLocation/host

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox AndroidSafari iOS5+Chrome AndroidWebView AndroidSamsung InternetOpera Android12.1+

host 获取器的步骤如下

  1. url当前对象WorkerGlobalScope 对象url

  2. 如果 urlhost 为空,则返回空字符串。

  3. 如果 urlport 为空,则返回 urlhost序列化后的结果。

  4. 返回 urlhost序列化后的结果,后面跟着 ":" 和 urlport序列化后的结果。

WorkerLocation/hostname

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox AndroidSafari iOS5+Chrome AndroidWebView AndroidSamsung InternetOpera Android12.1+

hostname 获取器的步骤如下

  1. host当前对象WorkerGlobalScope 对象urlhost

  2. 如果 host 为空,则返回空字符串。

  3. 返回 host序列化后的结果。

WorkerLocation/port

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox AndroidSafari iOS5+Chrome AndroidWebView AndroidSamsung InternetOpera Android12.1+

port 获取器的步骤如下

  1. port当前对象WorkerGlobalScope 对象urlport

  2. 如果 port 为空,则返回空字符串。

  3. 返回 port序列化后的结果。

WorkerLocation/pathname

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox AndroidSafari iOS5+Chrome AndroidWebView AndroidSamsung InternetOpera Android12.1+

pathname 获取器的步骤是返回 URL 路径序列化 当前对象WorkerGlobalScope 对象url 的结果。

WorkerLocation/search

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox AndroidSafari iOS5+Chrome AndroidWebView AndroidSamsung InternetOpera Android12.1+

search 获取器的步骤如下

  1. query当前对象WorkerGlobalScope 对象urlquery

  2. 如果 query 为空或空字符串,则返回空字符串。

  3. 返回 "?",后面跟着 query

WorkerLocation/hash

所有当前引擎均支持。

Firefox3.5+Safari4+Chrome4+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox AndroidSafari iOS5+Chrome AndroidWebView AndroidSamsung InternetOpera Android12.1+

hash 获取器的步骤如下

  1. fragment当前对象WorkerGlobalScope 对象urlfragment

  2. 如果 fragment 为空或空字符串,则返回空字符串。

  3. 返回 "#",后面跟着 fragment