Proyecto no brillante? Entonces aprendamos trabajadores web ~

Mucho tiempo sin verte, alguien que ha desaparecido por mucho tiempo 靓仔está de vuelta. En cuanto a por qué este problema no ha ocurrido durante tanto tiempo, usemos el ajetreo como excusa. emmm, no, ¡ 忙昏头了es del tipo real! ! !

Está bien, no más tonterías.

Por lo general, mis amigos no dicen eso en el desarrollo diario del proyecto, simplemente están moviendo ladrillos y no hay puntos brillantes, ¡así que vamos ahora!

Hablemos hoy , esto es lo que le gusta web workeral entrevistador ~最最最性能优化

¿Por qué JavaScript es de un solo subproceso?

Es bien sabido que el JavaScriptlenguaje se caracteriza por 单线程, es decir, que solo se puede hacer una cosa a la vez. Entonces, ¿por qué JavaScriptno puede 多个线程haber? puede 提高效率_

El hilo único de JavaScript, en relación con su propósito. Como lenguaje de secuencias de comandos del navegador, JavaScriptel uso principal es interactuar con el usuario y manipular el DOM. Esto determina que solo puede ser 单线程, de lo contrario traerá problemas de sincronización muy complejos. Por ejemplo, suponga JavaScriptque hay dos subprocesos al mismo tiempo, un subproceso agrega contenido a un nodo DOM y el otro subproceso elimina este nodo, ¿qué subproceso debe tomar el navegador?

¿Qué es un trabajador web?

Para aprovechar 多核CPUla potencia informática, se HTML5propone un estándar Web Workerque permite la creación de scripts de JavaScript 多个线程, pero los subprocesos secundarios están completamente controlados por el subproceso principal y 不得操作DOM. Por lo tanto, este nuevo estándar no cambia la naturaleza de subproceso único de JavaScript.

En el subproceso de trabajo, aunque la suma del 直接操作dom节点objeto de la ventana no puede y no puede usarse , las cosas debajo del objeto de la ventana aún pueden usarse, como , etc.默认方法属性websocketindexedDB

workers主线程间的数据传递通过这样的消息机制进行——双方都使用postMessage() 方法发送各自的消息,使用 onmessage 事件处理函数来响应消息(消息被包含在Message事件的 data 属性中)。这个过程中数据并不是被共享而是被复制。

关于web worker的兼容性问题,在can i use中查找一轮后发现,基本目前所有主流的浏览器都支持了,因此放心食用,无需考虑兼容性的问题。

image-20220702000223372

小试牛刀

前面学习了那么多武功秘籍,少侠们,确定不来一展身手吗?

img

小羽这里简单的写了一个小demo,这个demo的内容就是递归获取斐波那契数列。会分为单线程多线程模式,然后分别测试运行20次fb方法所需要的时间。

  • 单线程模式:利用for循环直接执行20次fb,统计执行时间
  • 多线程模式:利用for循环,创建多个worker线程。并使用promise.all处理这些异步的worker线程,等待所有的worker执行完成后,统计执行时间
<!--
 * @Author: xiaoyu
 * @Description: 
 * @Date: 2022-05-08 08:40:54
 * @LastEditors: xiaoyu
 * @LastEditTime: 2022-06-29 23:19:40
-->
<!DOCTYPE html>
<html lang="en">
​
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>web worker</title>
</head>
​
<script>
    const number = 20 // 运行次数
​
    // 多线程测试
    function workerTest() {
        console.log('%c 开始多线程测试 ', 'color:#fff; background:#00897b ')
        const workerList = []
        for (let i = 0; i < number; i++) {
            const workerItem = new Promise((resolve, reject) => {
                const myWorker = new Worker('worker.js')
                myWorker.postMessage({
                    function: 'fb',
                    data: 43
                })
                myWorker.onmessage = (e) => {
                    resolve(e.data)
                    // 关闭worker线程
                    myWorker.terminate()
                }
            })
            workerList.push(workerItem)
        }
        console.time('worker多线程执行时间')
        Promise.all(workerList).then(res => {
            console.log(res)
            console.timeEnd('worker多线程执行时间')
        })
    }
​
    function singleTest() {
        console.log('%c 开始单线程测试 ', 'color:#fff; background:#00897b ')
        function fb(n) {
            if (n === 1 || n === 2) {
                return 1;
            }
            return fb(n - 1) + fb(n - 2)
        }
        console.time('单线程执行时间')
        for (let i = 0; i < number; i++) {
            const res = fb(43)
            console.log({
                data: res,
                name: 'single test'
            })
        }
        console.timeEnd('单线程执行时间')
    }
</script>
​
<body>
    <button onclick="singleTest()">单线程测试</button>
    <button onclick="workerTest()">多线程测试</button>
</body>
​
</html>
/*
 * @Author: xiaoyu
 * @Description: worker 线程
 * @Date: 2022-05-08 08:41:30
 * @LastEditors: xiaoyu
 * @LastEditTime: 2022-06-29 23:17:44
 */
​
// 方法对象
const funcObj = {
  fb: (n) => {
    if(n===1 || n ===2){
      return 1;
    }
    return funcObj.fb(n-1) + funcObj.fb(n-2)
  }
}
​
​
// onmessage事件
onmessage = function(e){
  const {data} = e;
  const res = funcObj[data.function](data.data)
  
  // 将获取的数据通过postMessage发送到主线程
  self.postMessage({
    data: res,
    name: 'worker test'
  })
  self.close()
}

打开任务管理器,点击单线程测试按钮进行单线程的测试。可以从下图发现,单线程的调用时间约为70s,cpu的调用基本上也就只是两个核心在切换工作,小羽在多次测试后,其实是有多个核心在切换工作,不过单一时间只有一个核心是在满载工作(递归获取斐波那契数列)。

image-20220630002226796

同样是打开任务管理器,然后点击多线程测试按钮。此时咱们的cpu就不再偷懒了,直接16线程满载运行,只需要7.9s就完成了20次递归获取斐波那契数列。

咱们简单的计算一下使用web worker多线程提升效果:(70750-7973)/7973 ≈7.87。即提升了7.87倍效率。当然这是在8核16线程上的电脑上跑了,如果在核心数不同的cpu上这个倍数也是会发生相应的变化

img

image-20220630002853364

在单页面应用中使用

通过上面的例子,是不是so easy呀?

好啦,那咱们就算掌握了web worker基本使用方法啦。

但是在react、vue等单页面应用中,webpack/vite通常会将js代码打包成一个js文件。因此通过上面的new Worker('worker.js')的方式来新建worker,将会报访问不到worker.js的错误。

所以,在单页面应用中,咱们该怎么使用web worker呢?

方案1:既然webpack/vite会将js的代码打包成一个js文件,那咱们不让它打包不就好了。而单页面应用的工程下,通常都是会有一个public的静态资源目录,咱们将worker.js放入其中即可。

方案2:webpack4及以下的版本可以使用worker-loader

方案3:webpack5/vite则可以使用new Worker(new URL('worker.js', import.meta.url))的方式

import React from 'react'
​
export default function WebWorkerTest() {
  const handleClick = () => {
    const number = 1
    const workerList = []
    console.log('%c 开始多线程测试 ', 'color:#fff; background:#00897b ')
    for (let i = 0; i < number; i++) {
      const workerItem = new Promise((resolve, reject) => {
        const myWorker = new Worker(new URL('../utils/fb.worker.ts', import.meta.url))
        myWorker.postMessage({
          function: 'fb',
          data: 43
        })
        myWorker.onmessage = (e) => {
          resolve(e.data)
          // 关闭worker线程
          myWorker.terminate()
        }
      })
      workerList.push(workerItem)
    }
    console.time('worker多线程执行时间')
    Promise.all(workerList).then(res => {
      console.log(res)
      console.timeEnd('worker多线程执行时间')
    })
  }
​
  return (
    <>
      <button onClick={handleClick}>vite/webpack5</button>
    </>
  )
}
​
// fb.worker.ts
// 方法对象
const funcObj = {
  fb: (n: number): number => {
    if (n === 1 || n === 2) {
      return 1;
    }
    return funcObj.fb(n - 1) + funcObj.fb(n - 2);
  },
};
​
// onmessage事件
onmessage = function (e) {
  const { data } = e;
  const res = funcObj[data.function](data.data);
​
  // 将获取的数据通过postMessage发送到主线程
  self.postMessage({
    data: res,
    name: "worker test",
  });
  self.close();
};
​

注意事项

虽然web worker可以调用cpu的多线程,从而提高咱们页面的性能。但是它不是随便使用的,如果滥用web worker可能不仅不会得到性能的提升,还可能造成性能的损耗

举一个简单的小栗子

如果咱们将递归获取斐波那契数列第n位的方法,将传入参数修改为第2位,这时候咱们再重跑单线程测试和多线程测试。

结果如下图,咱们可以发现单线程模式下,获取20次斐波那契数列第二位的时间仅需要1.5ms,而在多线程的情况下却需要78ms。这是为什么呢?因为咱们每次创建worker线程以及possmessage通信都是需要损耗一些性能以及时间的。因此web worker是不可以滥用的哦,日常开发中,建议在需要消耗比较多的cpu运算能力的时候酌情使用。

image-20220701233019032

小结

本文通过了几个简单的小栗子,带大家学习web worker的基本知识,使用方法,以及需要注意的事项。小伙伴们在日常的开发中可以按需尝试哦,让自己的项目中多些亮点,也可以让面试官眼前一亮哦。

如果看这篇文章后,感觉有收获的小伙伴们可以点赞+关注哦~

img

如果想和小羽交流技术可以加下wx,也欢迎小伙伴们来和小羽唠唠嗑,嘿嘿~

ps:本文原创,转载请标明出处

Supongo que te gusta

Origin juejin.im/post/7117774868187185188
Recomendado
Clasificación