前端培训中级阶段24Web-Workers-多线程20191107期

6次阅读

共计 2674 个字符,预计需要花费 7 分钟才能阅读完成。

前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每周四)。

JS 是 单线程 事件循环 模型。使用上来说如果有 大量 高强度 的计算会导致 UI 渲染进程 卡顿。
按照 FPS: 60 来计算 1000ms/60 = 16.666ms。我们一段程序的占用时间需要低于16.666ms

Web Workers 用法

new Worker(url),将会在 worker 线程中运行代码,该环境 与主线程不同

因为并不是浏览器环境,所以 DOM、window 是无法使用的。当然也有一些 API 可以使用 localstroage、websocket、indexDB、XMLHttpRequest 等是没问题的。

同时分为两种环境。

  1. 独享模式:DedicatedWorkerGlobalScope(一个专用线程对应一个主线程)
  2. 共享模式:SharedWorkerGlobalScope(一个共享线程对应多个主线程)

主线程 worker 线程 之间使用 postMessage() 方法来发送信息,通过 onmessage 这个事件监听来接收信息。

数据的传输方式为 传递副本,而不是直接共享数据 。所以也导致有时传递的损耗高于计算的损耗。
当然,也支持整体内存移交给 worker,不过这样使用的话,主线程就访问不了这块内容了。
测试地址

worker

独享线程(Dedicated Web Worker),只能由创建时的主线程使用。

//html
wk = new Worker('/static/workers/1190000020913212.js');
demo2.addEventListener('click', function(){wk.postMessage(str)
})

//1190000020913212.js
onmessage = function(e) {
    var str = e.data;
    console.time('reportString');
    console.log(str.length, str.split('').join('+').length)
    console.timeEnd('reportString');
}

Shared Worker

同域 多个窗口 多个脚本 运行时,可以用于通信。
比如 iframe、标签页。

// html
swkPIP = new SharedWorker('/static/workers/1190000020913212-SharedWorker-pip.js');
swkPIP.port.start();
swkPIP.port.postMessage(args)

// worker.js
var portArr = [];
onconnect = function(e) {var port = e.ports[0];
    portArr.push(port)
    port.addEventListener('message', function(e) {console.log(self, e, port)
      if(e.data.type == 'private'){port.postMessage(['pip', e.data]);    
      }else if(e.data.type == 'public'){portArr.forEach(v=>v.postMessage(['pip', e.data]))
      }else if(e.data.type == 'publicNoSelf'){portArr.forEach(v=>v!=port&&v.postMessage(['pip', e.data]))
      }
    });
    port.start(); // Required when using addEventListener. Otherwise called implicitly by onmessage setter.}

从实现代码中可以看到。
Shared Worker 需要使用 prot 通道。还需要 start() 开启通道。

可以实现的效果

  1. 跨窗口通信
  2. 比如说有一些轮询的接口,在多窗口的场景中会形成密集的访问。如果我们把请求转入 Shared Worker,Shared Worker 中做节流。拉回数据再通知各个窗口。
  3. 比如用户在一个窗口中登录,把登录状态通知给其他窗口。

Service Workers

用于 浏览器 服务器 之前的代理服务。可以实现 离线访问 拦截请求 更新缓存 等。
navigator.serviceWorker.register('/static/js/sw-20190621.js')

其他事项

通过 URL.createObjectURL()创建 URL 对象,可以实现创建内嵌的 worker

开启 worker 需要一个 同源 的 URL。有时我们也不需要一个这样的文件,那么我们就可以通过一个指向内存的 URL。

// worker.js
var str = `
    var i = 0;
    function test(){postMessage(++i);
        setTimeout(test, 1000);
    }
    test();
`;
// html
var blob = new Blob([str]);
var wk = new Worker(window.URL.createObjectURL(blob));

通过转让对象来传递数据

默认情况下,主线程与 worker 之前的数据传递是通过 拷贝 ,也就是 JSON.stringify() 之后再发送,接受使用 JSON.parse()处理。
这样使用起来在会损耗一部分在 序列化 反序列化 中。
所以 worker 给我们提供了一种更高效的方式,将整块数据传入(整块内存移交,再源环境将不可访问)。

myWorker.postMessage(uInt8Array, [uInt8Array]);

通过 postMessage 的第二个入参,我们把 uInt8Array 对象整个发送出去。

执行环境的上下文

我们都知道 浏览器环境的上下文 Node 环境的上下文 是不一样的,其中有一些是浏览器环境中独有的。worker 环境也是不一样。
其中 self 指向当前环境的 global,与 Window 不是同一个对象,而是 WorkerGlobalScope

微信公众号:前端 linong

参考文献

  1. 前端培训目录、前端培训规划、前端培训计划
  2. https://www.sitepen.com/blog/the-return-of-sharedarraybuffers-and-atomics/
  3. JavaScript 性能利器 —— Web Worker
正文完
 0