活文档 — 最后更新于 2024年9月12日
所有当前引擎均支持。
本节是非规范性的。
本规范定义了一个 API,用于在后台独立于任何用户界面脚本运行脚本。
这允许执行长时间运行的脚本,而不会被响应点击或其他用户交互的脚本中断,并允许执行长时间任务而不会让步以保持页面响应。
工作线程(此处将这些后台脚本称为工作线程)相对重量级,并且不打算大量使用。例如,为四百万像素图像的每个像素启动一个工作线程是不合适的。以下示例展示了一些工作线程的适当用法。
通常,工作线程预计是长期存在的,具有较高的启动性能成本和较高的每个实例内存成本。
本节是非规范性的。
工作线程可以有多种用途。以下小节展示了这些用法的各种示例。
本节是非规范性的。
工作线程最简单的用途是执行计算量大的任务,而不会中断用户界面。
在此示例中,主文档生成一个工作线程来(天真地)计算素数,并逐步显示最近找到的素数。
主页面如下
<!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()
方法用于在找到素数时向页面发送消息。
本节是非规范性的。
我们到目前为止的所有示例都展示了运行 经典脚本 的工作线程。工作线程可以使用 模块脚本 实例化,这具有以下好处:能够使用 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 ;
}
};
所有当前引擎均支持。
本节是非规范性的。
本节使用 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' );
}
}
本节是非规范性的。
在此示例中,可以打开多个窗口(查看器),这些窗口都查看同一张地图。所有窗口共享相同的地图信息,一个工作线程协调所有查看器。每个查看器可以独立移动,但如果它们在地图上设置任何数据,则所有查看器都会更新。
主页面并不有趣,它仅仅提供了一种打开查看器的方法
<!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”消息并命名另一个查看器时,它会在两者之间建立直接连接,以便这两个查看器可以直接通信,而无需工作线程代理所有消息。
本节是非规范性的。
随着多核 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();
}
它们在两个事件中接收两个数字,执行为指定的数字范围执行的计算,然后将结果报告回父级。
本节是非规范性的。
假设提供了一个加密库,它提供了三个任务
库本身如下
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 的情况下委托给子工作线程,即使它正在使用消息通道接受数据。
本节是非规范性的。
创建工作线程需要一个指向 JavaScript 文件的 URL。 Worker()
构造函数只用指向该文件的 URL 作为其参数调用;然后创建一个工作线程并返回。
var worker = new Worker( 'helper.js' );
如果希望工作线程脚本被解释为 模块脚本 而不是默认的 经典脚本,则需要使用稍微不同的签名。
var worker = new Worker( 'helper.mjs' , { type: "module" });
本节是非规范性的。
专用工作线程在后台使用 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
所有当前引擎均支持。
本节是非规范性的。
共享工作线程由用于创建它的脚本的 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
};
本标准定义了两种工作线程:专用工作线程和共享工作线程。专用工作线程一旦创建,就会与其创建者关联,但可以使用消息端口从专用工作线程与多个其他浏览上下文或工作线程进行通信。另一方面,共享工作线程是命名的,一旦创建,在同一 来源 中运行的任何脚本都可以获取对该工作线程的引用并与其通信。 服务工作线程 定义了第三种。 [SW]
全局范围是工作线程的“内部”。
WorkerGlobalScope
通用接口所有当前引擎均支持。
[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
充当特定类型工作线程全局范围对象的基类,包括 DedicatedWorkerGlobalScope
、SharedWorkerGlobalScope
和 ServiceWorkerGlobalScope
。
一个 WorkerGlobalScope
对象具有一个关联的 所有者集(一个 集合,包含 Document
和 WorkerGlobalScope
对象)。它最初为空,并在创建或获取工作线程时填充。
它是一个 集合,而不是单个所有者,以适应 SharedWorkerGlobalScope
对象。
一个 WorkerGlobalScope
对象具有一个关联的 类型(“classic
”或“module
”)。它在创建期间设置。
一个 WorkerGlobalScope
对象具有一个关联的 url(null 或 URL)。它最初为 null。
一个 WorkerGlobalScope
对象具有一个关联的 名称(一个字符串)。它在创建期间设置。
WorkerGlobalScope
的每个子类,其 名称 具有不同的语义。对于 DedicatedWorkerGlobalScope
实例,它只是一个开发人员提供的名称,主要用于调试目的。对于 SharedWorkerGlobalScope
实例,它允许通过 SharedWorker()
构造函数获取对通用共享工作线程的引用。对于 ServiceWorkerGlobalScope
对象,它没有意义(因此根本不会通过 JavaScript API 公开)。
一个 WorkerGlobalScope
对象具有一个关联的 策略容器(一个 策略容器)。它最初是一个新的 策略容器。
一个 WorkerGlobalScope
对象具有一个关联的 嵌入器策略(一个 嵌入器策略)。
一个 WorkerGlobalScope
对象具有一个关联的 模块映射。它是一个 模块映射,最初为空。
一个 WorkerGlobalScope
对象具有一个关联的 跨源隔离功能 布尔值。它最初为 false。
workerGlobal.self
所有当前引擎均支持。
workerGlobal.location
所有当前引擎均支持。
WorkerLocation
对象。workerGlobal.navigator
所有当前引擎均支持。
WorkerNavigator
对象。
workerGlobal.importScripts(...urls)
WorkerGlobalScope/importScripts
所有当前引擎均支持。
self
属性必须返回 WorkerGlobalScope
对象本身。
location
属性必须返回 WorkerLocation
对象,其关联的 WorkerGlobalScope
对象 是 WorkerGlobalScope
对象。
虽然 WorkerLocation
对象是在 WorkerGlobalScope
对象之后创建的,但这并不成问题,因为它无法从脚本中观察到。
以下是必须支持的 事件处理程序(及其对应的 事件处理程序事件类型),作为 事件处理程序 IDL 属性,由实现 WorkerGlobalScope
接口的对象。
事件处理程序 | 事件处理程序事件类型 |
---|---|
onerror 所有当前引擎均支持。 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
|
DedicatedWorkerGlobalScope
接口所有当前引擎均支持。
[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
所有当前引擎均支持。
dedicatedWorkerGlobal.postMessage(message [, transfer ])
DedicatedWorkerGlobalScope/postMessage
所有当前引擎均支持。
dedicatedWorkerGlobal.postMessage(message [, { transfer } ])
克隆 message 并将其传输到与 dedicatedWorkerGlobal 关联的 Worker
对象。 transfer 可以作为要传输而不是克隆的对象列表传递。
dedicatedWorkerGlobal.close()
DedicatedWorkerGlobalScope/close
所有当前引擎均支持。
中止 dedicatedWorkerGlobal。
name
获取器步骤是返回 this 的 名称。它的值表示使用 Worker
构造函数赋予工作线程的名称,主要用于调试目的。
DedicatedWorkerGlobalScope
对象上的 postMessage(message, transfer)
和 postMessage(message, options)
方法的行为就像在调用时,立即在端口上调用相应的 postMessage(message, transfer)
和 postMessage(message, options)
,使用相同的参数,并返回相同的返回值。
要 关闭工作线程,给定一个 workerGlobal,请执行以下步骤
将 workerGlobal 的 closing 标志设置为 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
|
SharedWorkerGlobalScope
接口所有当前引擎均支持。
[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
所有当前引擎均支持。
返回 sharedWorkerGlobal 的 名称,即传递给 SharedWorker
构造函数的值。多个 SharedWorker
对象可以通过重用相同的名称对应于同一个共享工作线程(和 SharedWorkerGlobalScope
)。
sharedWorkerGlobal.close()
所有当前引擎均支持。
中止 sharedWorkerGlobal。
name
获取器步骤为返回 this 的 name。其值表示可以使用 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
|
worker 事件循环的 任务队列仅具有事件、回调和网络活动作为 任务。这些 worker 事件循环 由 运行 worker 算法创建。
每个 WorkerGlobalScope
对象都有一个 closing 标志,该标志最初必须为 false,但可以通过以下处理模型部分中的算法设置为 true。
一旦 WorkerGlobalScope
的 closing 标志设置为 true,则 事件循环的 任务队列必须丢弃任何将添加到其中的进一步 任务(队列中已有的任务不受影响,除非另有说明)。实际上,一旦 closing 标志为 true,计时器停止触发,所有挂起的后台操作的通知都会被丢弃,等等。
worker 通过 消息通道 及其 MessagePort
对象与其他 worker 和 Window
通信。
每个 WorkerGlobalScope
对象 worker 全局范围 都有一个 worker 的端口列表,该列表包含所有与另一个端口纠缠且只有一个(且仅一个)端口由 worker 全局范围 拥有的 MessagePort
对象。此列表包括在 专用 worker 的情况下隐式 MessagePort
。
在创建或获取 worker 时给定 环境设置对象 o,要添加的相关所有者 取决于 o 指定的 全局对象 的类型。如果 o 的 全局对象 是 WorkerGlobalScope
对象(即,如果我们正在创建嵌套的专用 worker),则相关所有者是该全局对象。否则,o 的 全局对象 是 Window
对象,相关所有者是该 Window
的 关联的 Document
。
如果 worker 的 WorkerGlobalScope
的 所有者集 不 为空 或
WorkerGlobalScope
对象是 SharedWorkerGlobalScope
对象(即,worker 是共享 worker),并且此定义的第二部分允许共享 worker 在页面加载时存活一段时间,以防该页面要再次联系共享 worker。用户代理可以使用它作为一种方法,以避免在用户在该站点内从一个页面导航到另一个页面时重新启动站点使用的共享 worker 的成本。
如果其任何 所有者 都是 完全活动 的 Document
对象或 活动所需 worker,则称 worker 为 活动所需 worker。
如果 worker 是 活动所需 worker 并且它具有未完成的计时器、数据库事务或网络连接,或者其 worker 的端口列表 不为空,或者其 WorkerGlobalScope
实际上是 SharedWorkerGlobalScope
对象(即,worker 是共享 worker),则称 worker 为 受保护的 worker。
如果 worker 不是 活动所需 worker,但它是 允许的 worker,则称 worker 为 可挂起的 worker。
当用户代理要为具有 Worker
或 SharedWorker
对象 worker、URL url、环境设置对象 外部设置、MessagePort
外部端口 和 WorkerOptions
字典 选项 的脚本 运行 worker 时,它必须运行以下步骤。
如果 worker 是 SharedWorker
对象,则令 is shared 为 true,否则为 false。
令 owner 为给定 外部设置 的 要添加的相关所有者。
令 unsafeWorkerCreationTime 为 不安全的共享当前时间。
令 agent 为给定 外部设置 和 is shared 获取专用/共享 worker 代理 的结果。在该代理中运行这些步骤的其余部分。
令 领域执行上下文 为给定 agent 和以下自定义项 创建新领域 的结果
对于全局对象,如果 is shared 为 true,则创建一个新的 SharedWorkerGlobalScope
对象。否则,创建一个新的 DedicatedWorkerGlobalScope
对象。
令 worker 全局范围 为 领域执行上下文 的 Realm 组件的 全局对象。
这是上一步中创建的 DedicatedWorkerGlobalScope
或 SharedWorkerGlobalScope
对象。
使用 领域执行上下文、外部设置 和 unsafeWorkerCreationTime 设置 worker 环境设置对象,并令 内部设置 为结果。
将 worker 全局范围 的 name 设置为 选项 的 name
成员的值。
如果 is shared 为 true,则
如果 is shared 为 true,则令 destination 为“sharedworker
”,否则为“worker
”。
通过切换 选项 的 type
成员的值来获取 script
classic
"模块
"credentials
成员的值、内部设置,并根据如下定义的 onComplete 和 performFetch,获取模块工作线程脚本图。在这两种情况下,令 performFetch 为以下给定 request、isTopLevel 和 processCustomFetchResponse 的 执行获取钩子。
如果 isTopLevel 为假,则使用 processResponseConsumeBody 设置为 processCustomFetchResponse 获取 request,并中止这些步骤。
获取 request,其中 processResponseConsumeBody 设置为以下步骤,给定 响应 response 和 null、失败或 字节序列 bodyBytes
初始化工作线程全局作用域的策略容器,给定 工作线程全局作用域、response 和 内部设置。
如果在 工作线程全局作用域 上执行 为全局对象运行 CSP 初始化 算法返回 "Blocked
",则将 response 设置为 网络错误。 [CSP]
如果 工作线程全局作用域 的 嵌入器策略 的 值 与 跨源隔离兼容 且 is shared 为真,则将 agent 的 代理集群 的 跨源隔离模式 设置为 "逻辑
" 或 "具体
"。选择哪一个由 实现定义。
这实际上应该在创建代理集群时设置,这需要重新设计本节。
如果使用 工作线程全局作用域、外部设置 和 response 检查全局对象的嵌入器策略 的结果为假,则将 response 设置为 网络错误。
如果 agent 的 代理集群 的 跨源隔离模式 为 "具体
",则将 工作线程全局作用域 的 跨源隔离功能 设置为 true。
如果 is shared 为假且 owner 的 跨源隔离功能 为假,则将 工作线程全局作用域 的 跨源隔离功能 设置为 false。
如果 is shared 为假且 response 的 url 的 方案 为 "data
",则将 工作线程全局作用域 的 跨源隔离功能 设置为 false。
这是一个保守的默认值,在我们弄清楚工作线程(通常)以及 data:
URL 工作线程(特别是与它们的拥有者跨源)在权限策略上下文中将如何处理之前。有关更多详细信息,请参阅 w3c/webappsec-permissions-policy 问题 #207。
使用 response 和 bodyBytes 运行 processCustomFetchResponse。
在这两种情况下,令给定 script 的 onComplete 为以下步骤
如果 script 为 null 或 script 的 要重新抛出的错误 不为 null,则
在给定 worker 的 相关全局对象 的 DOM 操作任务源 上排队一个全局任务,以 触发名为 error
的事件 在 worker 上。
对 内部设置 运行 环境丢弃步骤。
中止这些步骤。
将 worker 与 工作线程全局作用域 关联。
令 内部端口 为 内部设置 的 领域 中的 新的 MessagePort
对象。
将 内部端口 与 工作线程全局作用域 关联。
纠缠 外部端口 和 内部端口。
创建一个新的 WorkerLocation
对象并将其与 工作线程全局作用域 关联。
关闭孤立工作线程:监视工作线程,以便在它不再是 受保护的工作线程 且不晚于它不再是 允许的工作线程 之前,将 工作线程全局作用域 的 closing 标志设置为 true。
挂起工作线程:监视工作线程,以便只要 工作线程全局作用域 的 closing 标志为 false 且工作线程是 可挂起的工作线程,用户代理就会挂起该工作线程中脚本的执行,直到 closing 标志切换到 true 或工作线程不再是 可挂起的工作线程。
设置 内部设置 的 执行就绪标志。
如果 script 是 经典脚本,则 运行经典脚本 script。否则,它是一个 模块脚本;运行模块脚本 script。
启用 外部端口 的 端口消息队列。
如果 is shared 为假,则启用工作线程的隐式端口的 端口消息队列。
如果 is shared 为真,则在给定 工作线程全局作用域 的 DOM 操作任务源 上 排队一个全局任务,以 触发名为 connect
的事件 在 工作线程全局作用域 上,使用 MessageEvent
,其中 data
属性初始化为空字符串,ports
属性初始化为包含 内部端口 的新 冻结数组,以及 source
属性初始化为 内部端口。
启用关联的 服务工作线程客户端 为 工作线程全局作用域 的 相关设置对象 的 ServiceWorkerContainer
对象的 客户端消息队列。
事件循环:运行由 内部设置 指定的 负责的事件循环,直到它被销毁。
由 事件循环 运行的 任务 处理事件或执行回调可能会因下面定义的 终止工作线程 算法而被 过早中止。
工作线程处理模型保持在此步骤,直到事件循环被销毁,事件循环在 closing 标志设置为 true 后发生,如 事件循环 处理模型中所述。
解开 工作线程的端口 列表中的所有端口。
当用户代理需要终止一个工作线程时,它必须并行于工作线程的主循环(上面定义的“运行一个工作线程”处理模型)执行以下步骤。
将工作线程的WorkerGlobalScope
对象的closing标志设置为true。
如果在WorkerGlobalScope
对象的相关代理的事件循环的任务队列中排队了任何任务,则丢弃它们而不进行处理。
中止当前在工作线程中运行的脚本。
如果工作线程的WorkerGlobalScope
对象实际上是一个DedicatedWorkerGlobalScope
对象(即工作线程是专用工作线程),则清空工作线程的隐式端口与其关联的端口的端口消息队列。
当工作线程停止成为活动所需工作线程,并且即使在closing标志设置为true后工作线程仍继续执行时,用户代理可能会调用终止工作线程算法。
每当工作线程的某个脚本中发生未捕获的运行时脚本错误时,如果该错误不是在处理先前的脚本错误期间发生的,则用户代理将为工作线程的WorkerGlobalScope
对象报告该错误。
AbstractWorker
混合interface mixin AbstractWorker {
attribute EventHandler onerror ;
};
以下是必须支持的事件处理程序(及其对应的事件处理程序事件类型),作为事件处理程序IDL属性,由实现AbstractWorker
接口的对象提供。
事件处理程序 | 事件处理程序事件类型 |
---|---|
onerror 所有当前引擎均支持。 Firefox44+Safari11.1+Chrome40+ Opera?Edge79+ Edge(旧版)17+Internet Explorer不支持 Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android? 所有当前引擎均支持。 Firefox29+Safari16+Chrome5+ Opera10.6+Edge79+ Edge (旧版)?Internet Explorer不支持 Firefox Android33+Safari iOS16+Chrome Android不支持WebView Android?Samsung Internet4.0–5.0Opera Android11–14 所有当前引擎均支持。 Firefox3.5+Safari4+Chrome4+ Opera10.6+Edge79+ Edge (旧版)12+Internet Explorer10+ Firefox Android?Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+ | error
|
要设置工作线程环境设置对象,给定一个JavaScript执行上下文execution context、一个环境设置对象outside settings和一个数字unsafeWorkerCreationTime。
令inherited origin为outside settings的源。
令realm为execution context的Realm组件的值。
令worker global scope为realm的全局对象。
令settings object为一个新的环境设置对象,其算法定义如下。
返回execution context。
返回worker global scope的模块映射。
返回worker global scope的URL。
如果worker global scope的URL的方案是“data
”,则返回一个唯一的不透明源,否则返回inherited origin。
返回worker global scope的策略容器。
返回worker global scope的跨源隔离功能。
返回使用worker global scope的跨源隔离功能对unsafeWorkerCreationTime进行粗化的结果。
将settings object的ID设置为一个新的唯一的字符串,创建URL设置为worker global scope的URL,顶级创建URL设置为null,目标浏览上下文设置为null,以及活动Service Worker设置为null。
如果worker global scope是一个DedicatedWorkerGlobalScope
对象,则将settings object的顶级源设置为outside settings的顶级源。
否则,将settings object的顶级源设置为实现定义的值。
有关正确定义此内容的最新信息,请参阅客户端存储分区。
将realm的[[HostDefined]]字段设置为settings object。
返回settings object。
Worker
接口所有当前引擎均支持。
[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
对象。scriptURL将在后台获取并执行,创建一个新的全局环境,worker表示该环境的通信通道。options可用于通过name
选项定义该全局环境的名称,主要用于调试目的。它还可以确保此新的全局环境支持JavaScript模块(指定type: "module"
),如果指定了该选项,还可以用于通过credentials
选项指定如何获取scriptURL。
worker.terminate()
所有当前引擎均支持。
worker.postMessage(message [, transfer ])
所有当前引擎均支持。
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)
构造函数时,用户代理必须运行以下步骤。
令compliantScriptURL为使用获取受信任类型兼容字符串算法,并传入TrustedScriptURL
、this 的相关全局对象、scriptURL、"Worker 构造函数
"和"script
"作为输入后得到的结果。
令外部设置为当前设置对象。
令工作者 URL为根据compliantScriptURL相对于外部设置进行URL 编码解析得到的结果。
可以使用任何同源 URL(包括blob:
URL)。也可以使用data:
URL,但它们会创建一个具有不透明源的工作者。
如果工作者 URL为失败,则抛出一个"SyntaxError
" DOMException
。
令工作者为一个新的Worker
对象。
令外部端口为外部设置的领域中的一个新的MessagePort
。
将外部端口与工作者关联。
并行运行此步骤 并行
根据工作者、工作者 URL、外部设置、外部端口和选项 运行工作者。
返回工作者。
SharedWorker
接口所有当前引擎均支持。
[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
对象。scriptURL 将在后台获取并执行,创建一个新的全局环境,sharedWorker 代表与该环境的通信通道。name 可用于定义该全局环境的名称。
sharedWorker = new SharedWorker(scriptURL [, options ])
返回一个新的SharedWorker
对象。scriptURL 将在后台获取并执行,创建一个新的全局环境,sharedWorker 代表与该环境的通信通道。options 可用于通过 name
选项定义该全局环境的名称。它还可以确保此新的全局环境支持 JavaScript 模块(指定 type: "module"
),如果指定了该模块,则还可以使用 credentials
选项指定如何获取scriptURL。请注意,尝试使用其 type
或 credentials
值与现有共享工作者不匹配的options构造共享工作者会导致返回的sharedWorker触发错误事件,并且不会连接到现有的共享工作者。
sharedWorker.port
所有当前引擎均支持。
返回sharedWorker的MessagePort
对象,该对象可用于与全局环境通信。
port
属性必须返回其构造函数分配给它的值。它代表用于与共享工作者通信的MessagePort
。
用户代理有一个关联的共享工作者管理器,它是启动一个新的并行队列的结果。
为了简单起见,每个用户代理只有一个共享工作者管理器。实现可以为每个源使用一个;这在观察上没有区别,并且可以实现更多的并发性。
当SharedWorker(scriptURL, options)
构造函数被调用时
令compliantScriptURL为使用获取受信任类型兼容字符串算法,并传入TrustedScriptURL
、this 的相关全局对象、scriptURL、"SharedWorker 构造函数
"和"script
"作为输入后得到的结果。
如果options是一个DOMString
,则将options设置为一个新的WorkerOptions
字典,其 name
成员设置为options的值,其他成员设置为其默认值。
令外部设置为当前设置对象。
令urlRecord为根据compliantScriptURL相对于外部设置进行URL 编码解析得到的结果。
可以使用任何同源 URL(包括blob:
URL)。也可以使用data:
URL,但它们会创建一个具有不透明源的工作者。
如果urlRecord为失败,则抛出一个"SyntaxError
" DOMException
。
令工作者为一个新的SharedWorker
对象。
令外部端口为外部设置的领域中的一个新的MessagePort
。
将外部端口分配给工作者的port
属性。
如果外部设置是安全上下文,则令调用者是否为安全上下文为 true;否则为 false。
令外部存储密钥为根据外部设置运行获取用于非存储目的的存储密钥得到的结果。
令工作者全局范围为 null。
对于SharedWorkerGlobalScope
对象列表中的每个范围迭代
令工作者存储密钥为根据范围的相关设置对象运行获取用于非存储目的的存储密钥得到的结果。
如果以下所有条件都为真
则
将工作者全局范围设置为范围。
中断.
data:
URL 会创建一个具有不透明源的工作者。会比较构造函数源和构造函数 URL,以便可以在源内使用相同的data:
URL 获取相同的SharedWorkerGlobalScope
对象,但不能用于绕过同源限制。
如果工作者全局范围不为 null,但用户代理已配置为不允许由工作者全局范围表示的工作者与脚本(其设置对象为外部设置)进行通信,则将工作者全局范围设置为 null。
例如,用户代理可以具有开发模式,该模式将特定顶级可遍历对象与所有其他页面隔离开,并且该开发模式中的脚本可能会被阻止连接到在普通浏览器模式下运行的共享工作者。
如果工作者全局范围不为 null,则检查工作者全局范围的类型和凭据是否与options的值匹配。如果不匹配,则将一个任务排队以触发名为error
的事件,并中止这些步骤。
如果工作者全局范围不为 null,则运行这些子子步骤
令设置对象为工作者全局范围的相关设置对象。
如果settings 对象处于安全上下文,则将workerIsSecureContext设置为true;否则设置为false。
如果workerIsSecureContext不等于callerIsSecureContext,则排队一个任务,在worker上触发一个名为error
的事件,并终止这些步骤。[SECURE-CONTEXTS]
将 worker 与 工作线程全局作用域 关联。
将内部端口设置为settings 对象的领域中新的MessagePort
。
将外部端口和内部端口关联。
使用DOM 操作任务源 排队一个任务,在工作线程全局范围上触发一个名为connect
的事件,使用MessageEvent
,其中data
属性初始化为空字符串,ports
属性初始化为一个新的仅包含内部端口的冻结数组,source
属性初始化为内部端口。
返回工作者。
interface mixin NavigatorConcurrentHardware {
readonly attribute unsigned long long hardwareConcurrency ;
};
self.navigator.hardwareConcurrency
返回用户代理可能可用的逻辑处理器数量。
navigator.hardwareConcurrency
属性的 getter 必须返回 1 到用户代理可能可用的逻辑处理器数量之间的数字。如果无法确定,则 getter 必须返回 1。
用户代理应倾向于公开可用的逻辑处理器数量,仅在存在用户代理特定限制(例如,创建工作线程数量的限制)或用户代理希望限制指纹识别可能性时使用较低的值。
importScripts(...urls)
方法的步骤如下
将urlStrings设置为« »。
对于urls中的每个url
将使用TrustedScriptURL
、this的相关全局对象、url、"Worker importScripts
"和"script
"作为参数调用获取受信任类型兼容字符串算法的结果追加到urlStrings中。
给定this和urlStrings 将脚本导入工作线程全局范围。
要给定一个WorkerGlobalScope
对象工作线程全局范围、一个urls的列表(包含标量值字符串),以及一个可选的执行获取钩子performFetch 将脚本导入工作线程全局范围。
将settings 对象设置为当前设置对象。
如果urls为空,则返回。
将urlRecords设置为« »。
对于urls中的每个url
将urlRecord设置为给定url(相对于settings 对象)编码解析URL的结果。
如果urlRecord为失败,则抛出一个"SyntaxError
"DOMException
。
追加urlRecord到urlRecords中。
对于urlRecords中的每个urlRecord
《服务工作线程》是一个使用其自己的执行获取钩子运行此算法的规范示例。[SW]
WorkerNavigator
接口所有当前引擎均支持。
WorkerGlobalScope
接口的navigator
属性必须返回WorkerNavigator
接口的一个实例,该接口表示用户代理(客户端)的身份和状态。
[Exposed =Worker ]
interface WorkerNavigator {};
WorkerNavigator includes NavigatorID ;
WorkerNavigator includes NavigatorLanguage ;
WorkerNavigator includes NavigatorOnLine ;
WorkerNavigator includes NavigatorConcurrentHardware ;
WorkerLocation
接口所有当前引擎均支持。
所有当前引擎均支持。
[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
对象)。
所有当前引擎均支持。
href
getter 的步骤是返回this的WorkerGlobalScope
对象的url,序列化后的结果。
所有当前引擎均支持。
origin
getter 的步骤是返回this的WorkerGlobalScope
对象的url的origin的序列化。
所有当前引擎均支持。
protocol
getter 的步骤是返回this的WorkerGlobalScope
对象的url的scheme,后跟":
"。
所有当前引擎均支持。
host
获取器的步骤如下
令 url 为 当前对象 的 WorkerGlobalScope
对象 的 url。
如果 url 的 host 为空,则返回空字符串。
所有当前引擎均支持。
hostname
获取器的步骤如下
令 host 为 当前对象 的 WorkerGlobalScope
对象 的 url 的 host。
如果 host 为空,则返回空字符串。
返回 host,序列化后的结果。
所有当前引擎均支持。
port
获取器的步骤如下
令 port 为 当前对象 的 WorkerGlobalScope
对象 的 url 的 port。
如果 port 为空,则返回空字符串。
返回 port,序列化后的结果。
所有当前引擎均支持。
pathname
获取器的步骤是返回 URL 路径序列化 当前对象 的 WorkerGlobalScope
对象 的 url 的结果。
所有当前引擎均支持。
search
获取器的步骤如下
令 query 为 当前对象 的 WorkerGlobalScope
对象 的 url 的 query。
如果 query 为空或空字符串,则返回空字符串。
返回 "?
",后面跟着 query。
所有当前引擎均支持。
hash
获取器的步骤如下
令 fragment 为 当前对象 的 WorkerGlobalScope
对象 的 url 的 fragment。
如果 fragment 为空或空字符串,则返回空字符串。
返回 "#
",后面跟着 fragment。