前言

  • 面试的时候有被问到一个场景:一个页面渲染程的需要计算上亿的次的计算,如何可以将页面尽量快的完成渲染

    • 一开始以为是考<script>写的位置(body的底部),或者defer属性(先下载渲染后再加载),但是似乎都不是面试官想要的答案
    • 直到学习到了Web Worker…

Web Worker

  • 概述:由浏览器提供的web Api,主要是为了个js提供多线程的能力,另开一线程执行代码,兼容性

  • 执行过程:使用代码new Worker('url')去加载url的代码(网络),然后在另外的线程执行url的代码,主线程和worker线程通过Worker.postMessage发送数据和window.onmessage接收数据来进行交互

  • 限制:同源政策,内容安全策略(指定)

  • 使用:

    • main.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // main.js 
    if (window.Worker) {
    const ww = new Worker('./worker.js');
    let first = document.querySelector('#first')
    ww.postMessage('go')
    // first.onchange = function (v) {
    // console.log(v.target.value)
    // ww.postMessage(v.target.value)
    // }
    ww.onmessage = function (e) {
    result = e.data;
    console.log('Message received from worker');
    first.value = result
    ww.terminate();
    }
    ww.onerror = function(e){
    console.log('get error %o',e)
    }
    } else {
    console.log('not support web worker')
    }
    • worker.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // worker.js
    onmessage = function (e) {
    console.log('Message received from main script');
    const start = new Date().getTime()
    let sum = 0
    for(let i=0;i<10000000000;i++){
    sum += i
    }
    console.log(new Date().getTime() - start)
    console.log('Posting message back to main script');
    postMessage(sum);
    }
    • 使用后关闭主线程worker.terminate(),或这worker内部close()
  • JavaScript是单线程的同时在浏览器中和GUI渲染的线程互斥,前面开头的提到的场景中:

    • 如果停下渲染html去执行计算,那么进入页面就会空白一段时间等js加载完再出现页面ui

    • 如果使用把script直接放到html的body标签的最后,页面ui会渲染出来先,但在渲染完成后到js计算完成之前,页面ui交互可能会报错(计算未完成);因为计算时间长,所以是可能在这段时间用户进行ui交互(可以加个“加载中”动画缓解用户焦虑,类似请求完成前一直loading)

      time = renderHtml + downloadJs + runJs

    • 如果使用<script>的defer,可以先并行下载后执行,相对上一点只是节省了一下下载时间(几乎忽略不计)

      time = max(renderHtml, downloadJs) + runJs

    • 如果使用web worker,先直接加载使用worker的js代码,然后渲染html;因为是另开线程来执行,所以不阻塞html渲染,页面ui应该会马上出来;因为页面和js计算基本同时执行,所以如果计算较大,使用Web Worker是最快的;

      time = max(renderHtml, downloadJs + runJs)

    • 当然无论如何,计算量大总是会有问题,所以加个加载动画是最好的,让用户看到页面又屏蔽用户的操作。

参考:

Web Worker 使用教程

使用 Web Workers

最后更新: 2021年07月16日 00:07

原始链接: https://idkhts.github.io/2021/01/19/Web%20Worker/