Web Worker 简介 是什么 ?

Web worker 是运行在后台的操作

Worker 是一个对象,通过构造函数 Worker 创建,参数就是一个 js 文件的路径;文件中的 js 代码将运行在 主线程之外的 Worker 线程

Worker 运行在另一个全局上下文中( self ),这个全局上下文不同于 window ,所以不能在 woker 中访问 window 和 DOM

该线程分为两种:dedicated worker 和 shared worker;dedicated worker 只能被初始化它的 js 上下文中使用;shared worker 可以在多个 js 上下文中使用。通常使用的 worker 是 dedicated worker,它的工作情况可以通过 chrome 的调试工具查看

为什么引入 woker ?

  • 浏览器中 JS 和 UI 公用一个线程, JS 计算过程中,不能响应 UI
  • Web Worker 是为了解决 JavaScript 在浏览器环境中没有多线程的问题
  • 最佳使用场景是执行一些开销较大的数据处理或计算任务

woker 如何工作 ?

  • 在 主线程 中创建一个 Worker 实例,通过监听 onmessage 事件获取消息,通过 postMessage 发送消息
  • 如下图:
  • pic
    worker

完整示例代码如下

  • 包含两个文件:1. 主线程文件 test.html 2. worker 线程所在文件
  • test.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>Web Worker</title>
    <style>
    </style>
    </head>
    <body>
    <input type="text" id="inp1" />
    <input type="text" id="inp2" />
    <button id="btn1">计算</button>
    <script>
    //Web Worker学习
    window.onload = function() {
    let btn1 = document.getElementById('btn1');
    let inp1 = document.getElementById('inp1');
    let inp2 = document.getElementById('inp2');
    btn1.onclick = function() {
    let n1 = inp1.value;
    let n2 = inp2.value;
    //第一步,创建一个Worker对象,指明url
    let w = new Worker('w1.js');
    //第二步,往Worker里面传入要处理的参数
    w.postMessage({n1,n2});
    //第六步,接收Worker返回的结果
    w.onmessage = function(ev) {
    alert(ev.data);
    };
    };
    };
    //在WEB端,主进程又叫 UI进程
    //子进程又叫 工作进程 (子进程是看不见的,只能完成计算,数据请求之类的操作)
    //Worker的优点:1 充分利用资源,多个进程同时工作 2 防止主进程卡住
    //Worker的缺点:1 不能执行任何UI操作,子进程只能执行计算类的任务
    //结论:Worder在工作中用的很少,因为在web端计算任务本来就少
    </script>
    </body>
    </html>
  • 同一目录下的 w1.js

    1
    2
    3
    4
    5
    6
    7
    8
    this.onmessage = function(ev) {
    //第三步,在Worker里面接收参数
    console.log(ev.data);
    //第四步,进行相关的操作
    let sum = parseInt(ev.data.n1) + parseInt(ev.data.n2);
    //第五步,将计算的结果返回
    this.postMessage(sum);
    };

Tips

  • 使用多少个 worker 合适 ? 遇到复杂的计算,需要开启多少worker才合适呢?一般的做法是参考navigator.hardwareConcurrency 这个属性,它表示机器支持的并行最大任务数。还有一种动态检测 Worker 数量的方法,有兴趣的话可以看:https://github.com/oftn-oswg/core-estimator
  • 优化 woker 与主线程通信开销 该段主要参考百度地图技术博客(https://mp.weixin.qq.com/mp/getmasssendmsg?__biz=MzIzNDE0NjMzOQ==#wechat_webview_type=1&wechat_redirect)
    Worker 与 主线程 之间的数据传递默认是通过结构化克隆(Structured Clone)完成的。数据量较大时,克隆过程会比较耗时,这会影响 postMessage 和 onmessage 函数的执行时间。
    解决的办法一是先通过 JSON.stringify 将对象序列化,接收之后再用 JSON.parse 还原。因为:stringfiy + 传递字符串的耗时 < 传递对象的耗时

参考网址: