win7共享连接数限制_JavaScript Web Workers(三)共享Worker

c98bd68b15388c6395df22717c9cba99.png

共享web worker(或共享worker)的行为类似专用worker,但是它可被多个可信执行环境访问。例如,两个同一个源的不同标签可以访问同一个worker。共享worker和worker的外部和内部消息接口有轻微不同。

当开发者想要减少计算开销,允许多个执行环境共享一个worker时,共享worker很有用。例如用一个共享worker为多个同源页面管理发送和接收消息的websocket。同源环境之间也可以通过共享worker进行通讯。

共享Worker基础

从行为上来说,共享worker可以认为是专用worker的扩展。worker的创建、选项,安全限制和importScripts()方法都是相同的。共享worker也在独立的执行环境中运行,也只能和其他环境进行异步通讯。

创建共享Worker

和专用worker一样,创建共享worker的最一般方法是通过加载JavaScript文件。给SharedWorke构造函数提供文件路径,它就在后台异步加载脚本并实例化worker。

下面的简单里子从绝对路径文件创建了一个空的共享worker:

EMPTYSHAREDWORKER.JS
    // empty JS worker file
MAIN.JS
    console.log(location.href); // "https://example.com/"
    const sharedWorker = new SharedWorker(
        location.href + 'emptySharedWorker.js');
    console.log(sharedWorker); // SharedWorker {}

前面的例子可以改为使用相对路径;然而,这要求main.js的执行路径与emptySharedWorker.js的路径相同才能载入脚本。

const worker = new Worker('./emptyWorker.js');
console.log(worker); // Worker {}

共享worker也可以从内联脚本创建,但这么做的意义不大:每个从内联脚本字符串创建的blob都被赋予一个唯一的 in-browser URL,因此由内联脚本创建的共享worker总是独一无二的。在下一节说明原因。

共享Worker的身份和单个占用

共享worker和专用worker的一个重要区别是:Worker()总是创建新的worker实例,SharedWorker()构造函数仅在没有具有相同身份的worker实例存在时才会创建一个新的。如果一个共享worker和已存在的共享worker身份匹配,那么它就会和已存在的共享worker形成一个新的连接。

共享worker身份由解析脚本URL,worker名,和文档源确定。例如,下面的脚本会实体化单个共享worker并加入两个连接。

// Instantiates single shared worker
// - Constructors called all on same origin
// - All scripts resolve to same URL
// - All workers have same name
new SharedWorker('./sharedWorker.js');
new SharedWorker('./sharedWorker.js');
new SharedWorker('./sharedWorker.js');

类似的,因为所有下面的三个脚本解析为相同的URL,仅创建了一个共享worker:

// Instantiates single shared worker
// - Constructors called all on same origin
// - All scripts resolve to same URL
// - All workers have same name
new SharedWorker('./sharedWorker.js');
new SharedWorker('sharedWorker.js');
new SharedWorker('https://www.example.com/sharedWorker.js');

因为可选的worker名是共享worker身份的一部分,使用不同的worker名(一个名为’foo‘,一个名为’bar‘)会让浏览器强制创建多个共享worker,尽管它们的源和脚本URL相同

// Instantiates two shared workers
// - Constructors called all on same origin
// - All scripts resolve to same URL
// - One shared worker has name 'foo', one has name 'bar'
new SharedWorker('./sharedWorker.js', {name: 'foo'});
new SharedWorker('./sharedWorker.js', {name: 'foo'});
new SharedWorker('./sharedWorker.js', {name: 'bar'});

顾名思义,共享worker可以在标签,窗口,iframe或其他运行在同一个源的worker之间共享。因此,下面运行在多个标签上的脚本将在首次执行时创建一个worker,后续的执行将连接到同一个worker:

// Instantiates single shared worker
// - Constructors called all on same origin
// - All scripts resolve to same URL
// - All workers have same name
new SharedWorker('./sharedWorker.js');

共享worker的身份是URL限定的,因此下面将创建两个共享worker,即使加载的是同一个脚本。

// Instantiates two shared workers
// - Constructors called all on same origin
// - '?' token differentiates URLs
// - All workers have same name
new SharedWorker('./sharedWorker.js');
new SharedWorker('./sharedWorker.js?');

如果该脚本是在两个不同的标签运行,仍然是创建两个共享worker。每个构造函数都检查匹配的共享worker,如果存在就连接它。

使用SharedWorker对象

由SharedWorker()返回的SharedWorker对象可用作与新创建的专用worker通讯的单点。它可用作在worker和父环境之间通过MessagePort传输信息,并捕获专用worker发出的错误事件。

SharedWorker对象支持下面属性:

(1)onerror—可以分配一个事件处理程序,当ErrorEvent类型的错误从worker上冒泡时调用。

  • 当错误从worker内部抛出时,事件发生。
  • 事件也可以使用sharedWorker.addEventListener('error',handler)方法处理。

(2)port—专用的MessagePort,用于和共享worker通讯。

SharedWorkerGlobalScope

在共享worker内部,全局作用域是SharedWorkerGlobalScope的一个实例。它继承自WorkerGlobalScope ,因此包括它的所有属性和方法。和专用worker一样,共享worker可以通过self访问全局作用域。

(1)SharedWorkerGlobalScope 扩展了WorkerGlobalScope,具有如下属性和方法:

(2)name—可选的字符串标识符,可以提供给SharedWorker构造函数。

(3)importScripts()—用于加载任意数量的脚本进入worker。

(4)close()—对应于worker.terminate()。它用于立即终止worker。没有提供给worker清理的机会;脚本立即结束。

(5) onconnect—设置的事件处理程序用于当和共享worker有新的连接时。connect事件包括一个MessagePort实例的端口数组,它可用于发送消息回父环境。

  • 通过worker.port.onmessage 或 worker.port.start()都能触发connect事件。
  • 这一事件也可以使用sharedWorker.addEventListener('connect', handler)方法处理.
注意:根据浏览器的实现不同,在SharedWorker内部向控制台log记录可能不会向默认的浏览器控制台打印。

理解共享Worker 的生命周期

共享worker的生命周期与专用worker的生命周期有相同的阶段。不同在于,专用worker和单个页面密不可分,而只要还有一个环境保持和共享worker连接,共享worker就会持续。

考虑下面的脚本,每次执行都创建一个专用worker:

new Worker('./worker.js');

下面的表格详细说明了当三个标签依次打开关闭时,它们生成的worker发生了什么。

12fa18d85d52f401c4a01047234c8da2.png

如表中所示,脚本的执行次数,打开标签的个数,在运行的worker数是相等的。接下来,考虑如下的简单代码,它每次执行将创建或连接到一个共享worker。

new SharedWorker('./sharedWorker.js');

下面的表格详细说明了当三个标签依次打开关闭时,它们生成的worker发生了什么。

eda3b5cb939b439bd33a50c320c7e95b.png

如表中所示,在tab2和tab3调用new SharedWorker()将连接到已存在的worker。当连接从worker添加或移除都会检查连接的总数。单连接数变为0时,终止worker。

重要的一点,不能人为编程终止共享worker。注意SharedWorker对象上没有terminate()方法。此外,在共享worker端口(本章后面讨论)上调用close()不会触发worker的终止,即便只有一个端口连接到该worker。

SharedWorker对象的“连接”与相应的MessagePort 或MessageChannel的状态无关。一旦建立和共享worker的连接,连接的管理就由浏览器负责。建立的连接将持续在页面的生命周期中持续,只有页面销毁,共享worker上没有更多连接的时候,浏览器终止该worker。

连接共享Worker

每次调用SharedWorker构造函数时,在共享worker内部触发connect事件,包括创建worker。下面的例子说明了这一点,这里构造函数在循环中调用:

SHAREDWORKER.JS
    let i = 0;
    self.onconnect = () => console.log(`connected ${++i} times`);
MAIN.JS
    for (let i = 0; i < 5; ++i) {
        new SharedWorker('./sharedWorker.js');
    }
// connected 1 times
// connected 2 times
// connected 3 times
// connected 4 times
// connected 5 times

connect事件出现时,SharedWorker 构造函数隐式创建一个MessageChannel ,并给传入一个对SharedWorker 实例来说是唯一的MessagePort所有权。这个MessagePort在connect事件对象内部可用ports数组访问。因为conncet事件仅代表单个连接,ports数组的长度刚好为1。

下面说明如何访问事件的ports数组。这里使用Set保证跟踪的对象没有重复。

SHAREDWORKER.JS
    const connectedPorts = new Set();
    self.onconnect = ({ports}) => {
    connectedPorts.add(ports[0]);
    console.log(`${connectedPorts.size} unique connected ports`);
    };
MAIN.JS
    for (let i = 0; i < 5; ++i) {
        new SharedWorker('./sharedWorker.js');
    }
// 1 unique connected ports
// 2 unique connected ports
// 3 unique connected ports
// 4 unique connected ports
// 5 unique connected ports

重要的一点,共享worker在建立和删除连接时行为不同。每个新的SharedWorker连接都触发一个事件,但SharedWorker实例断开连接时(例如页面关闭)没有对应的事件。

在前面的例子中,当页面连接并断开同一个共享worker时,connectedPorts Set将会被“死去的”端口污染,而无法鉴别它们。一种解决办法是,在页面将要被销毁前的beforeunload 事件发生时,显式的发送一条teardown消息,这可以让共享worker做清理工作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值