webWorker解决单线程中的一些小问题和性能优化

这篇具有很好参考价值的文章主要介绍了webWorker解决单线程中的一些小问题和性能优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

js是单线程这是大家都知道,为了防止多个线程同时操作DOM,这个导致一个复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准。

webWorker

web worker是 HTML5 标准的一部分,这一规范定义了一套 API,允许我们在 js 主线程之外开辟新的 Worker 线程,并将一段 js 脚本运行其中,它赋予了开发者利用 js 操作多线程的能力。

因为是独立的线程,Worker 线程与 js 主线程能够同时运行,互不阻塞。所以,在我们有大量运算任务时,可以把运算任务交给 Worker 线程去处理,当 Worker 线程计算完成,再把结果返回给 js 主线程。这样,js 主线程只用专注处理业务逻辑,不用耗费过多时间去处理大量复杂计算,从而减少了阻塞时间,也提高了运行效率,页面流畅度和用户体验自然而然也提高了。

常见问题

场景一:人脸识别和活体检测

在这两个场景中会通过ws实时上传图片(加密)

这里面主要是两个问题:
(1)图片加密频繁操作,导致占用主线程,页面出现卡顿卡死情况

(2)定时器延时,上面操作导致定时器并不是开始规定的5秒可能是7秒8秒

场景二:大规模数据上传

比如有个项目前端拿到很大量据需要,需要后处理后再上传服务器。

处理数据可能会占用主线程导致页面显示不流程,卡顿等情况

场景三:请求过于频繁,读写操作过多

同上

场景四:视频音频转码、转格式、加密等操作。

和场景一一样,大量耗费计算资源。阻塞主线程,导致页面流畅度下降,响应降低。

解决以上问题可以考虑使用webwork

怎么使用?

要使用先了解它
Web Worker的限制

1、在 Worker 线程的运行环境中没有 window 全局对象,也无法访问 DOM 对象。

2、Worker中只能获取到部分浏览器提供的 API,如定时器、navigator、location、XMLHttpRequest等。

3、由于可以获取XMLHttpRequest 对象,可以在 Worker 线程中执行ajax请求。

4、每个线程运行在完全独立的环境中,需要通过postMessage、 message事件机制来实现的线程之间的通信。

worker.postMessage:

向 worker 的内部作用域发送一个消息,消息可由任何 JavaScript 对象组成

worker.terminate:

立即终止 worker。该方法并不会等待 worker 去完成它剩余的操作;worker 将会被立刻停止

worker.onmessage:

当 worker 的父级接收到来自其 worker 的消息时,会在 Worker 对象上触发message 事件

worker.onerror:

当 worker 出现运行中错误时,它的 onerror事件处理函数会被调用。它会收到一个扩展了

ErrorEvent 接口的名为 error 的事件

我直接上代码,我常用(不,是只会)vue,看在项目是怎么使用的。

我写了几个worker.js

1:msg.worker.js 为了消息推送

onmessage = function(e) {
  console.log(e)
  postMessage(e.data.num);
  // close();
}
import MsgWorker from "./demo/msg.worker"; 

// mounted  初始化
this.msgWorker = new MsgWorker ();
 
this.worker.onmessage = (event) => {
   console.log(event.data);
   this.result = event.data;
   console.log("主线程收到回复,即将关闭worker连接", this.index);
   // this.worker.terminate();
};

methods: {
   useWorker() {
      this.msgWorker.postMessage({ text: "当前时间:", num: Date.now() });
    },
  },

想想:他能干嘛?

利用策略模式,全局监听操作,和vue Bus 功能一样,使用在页面交互功能一样。

有人说和写个全局方法不一样吗?
不一样,它不占用js的主线程

2:time.worker.js添加定时器

onmessage = function (e) {
  setTimeout(() => {
    postMessage(Date.now());
  }, e.data.time);
  // close();
};
import TimeWorker from "./demo/time.worker";

//初始化mounted
mounted() {
    this.timeWorker = new TimeWorker();
    this.timeWorker.onmessage = (event) => {
      this.result = event.data;
      console.log("定时完成");
    };
  },
//方法调用
 methods: {
    useWorker() {
      this.timeWorker.postMessage({ time: 10000 });
    },
  },

 定时器能干嘛?如果进来先执行 上面定时器useWorker()  再执行下面计算demo()
 它能定时输出时间,不被js主线程阻塞影响。

 demo() {
  console.log("Start", Date.now());
  let i = 0;
  for (let index = 0; index < 100000000000; index++) {
    i += index;
  }
  console.log(i, Date.now())
  console.log("End", Date.now());
}

3:canvas.worker.js canvas绘制

<template>
  <div class="canvas-demo">
    <button @click="makeWorker">开始绘图</button>
    <canvas id="myCanvas" width="300" height="150"></canvas>
  </div>
</template>

<script>
import Worker from "./demo/canvas.worker";

export default {
  methods: {
    makeWorker() {
      let worker = new Worker();
      let htmlCanvas = document.getElementById('myCanvas');
      // 使用canvas的transferControlToOffscreen函数获取一个OffscreenCanvas对象
      let offscreen = htmlCanvas.transferControlToOffscreen();
      // 注意:第二个参数不能省略
      worker.postMessage({ canvas: offscreen }, [offscreen]);
    }
  }
};
</script>

<style lang="less">
.canvas-demo {
  padding: 20px;
}
</style>

canvas.worker.js

onmessage = function (e) {
  // 使用OffscreenCanvas(离屏Canvas)
  let canvas = e.data.canvas;
  // 获取绘图上下文
  let ctx = canvas.getContext('2d');
  // 绘制一个圆弧
  ctx.beginPath(); // 开启路径
  ctx.arc(150, 75, 50, 0, Math.PI * 2);
  ctx.fillStyle = '#333333'; //设置填充颜色
  ctx.fill(); //开始填充
  ctx.stroke();
};

4:xhrWorker.js接口调用 

<template>
  <div>
    <button @click="useWorker">开始线程</button>
  </div>
</template>

<script>
import { fetchApi} from "./demo/xhrWorker.js";
export default {
  data() {
    return {
      worker: null,
    };
  },
  mounted() {
    const blob = fetchApi();
    this.worker = new Worker(blob); // 使用上面import进来的js,名字为 demo.worker.worker.js,不可配置,路径相对比较灵活,需要worker-loader
    this.worker.onmessage = (event) => {
      console.log(event.data);
      console.log("主线程收到回复,即将关闭worker连接", event.data);
      // this.worker.terminate();
    };
    this.worker.onerror = (event) => {
      console.log(event.data);
    };
  },
  methods: {
    useWorker() {
      this.worker.postMessage({
        url: `http://xx.xx.xx.xx:8888/login`,
        data: {
          password: "admin",
          username: "admin123456",
        },
        responseType: "json",
        method: "POST",
        id: Date.now(),
      });
    },
  },
  // 页面关闭,如果还没有计算完成,要销毁对应线程
  beforeDestroy() {},
};
</script>

 xhrWorker.js  我尝试将他写成 xhr.worker.js 结果获取不到fetchApi方法,可以注意下

export function fetchApi() {
  const workerCode = `
self.addEventListener('message', async function (e) {
  const { url, data, responseType, method, id } = e.data
  const xhr = new XMLHttpRequest()
  xhr.open(method, url, true)
  xhr.responseType = responseType
  xhr.setRequestHeader('Content-Type', 'application/json')

  xhr.onload = function () {
    if (xhr.status === 200) {
      self.postMessage({
        xhrRes: xhr.response, 
        id
      })
    } else {
      self.postMessage({ error: 'error' })
    }
  }

  xhr.onerror = function () {
    self.postMessage({ error: 'error' })
  }

  xhr.send(JSON.stringify(data))
})
`

  const blob = new Blob([workerCode], { type: 'application/javascript' })
  const blobUrl = URL.createObjectURL(blob)

  return blobUrl
}

 5:dataWorker.js数据计算 

<template>
  <div>
    <div class="data-lsit">
      <div class="=data-item" v>
          <span>数据</span>

      </div>

    </div>


    <button @click="makeWorker">开始线程</button>
    <!--在计算时 往input输入值时 没有发生卡顿-->
    <p><input type="text" /></p>
  </div>
</template>

<script>
import Worker from "./demo/math.worker";

export default {
  data() {
    // 模拟数据
    let arr = new Array(20).fill(1).map(() => Math.random() * 10000);
    let weightedList = new Array(100000)
      .fill(1)
      .map(() => Math.random() * 10000);
    let calcList = [
      { type: "sum", name: "总和" },
      { type: "average", name: "算术平均" },
      { type: "weightedAverage", name: "加权平均" },
      { type: "max", name: "最大" },
      { type: "middleNum", name: "中位数" },
      { type: "min", name: "最小" },
      { type: "variance", name: "样本方差" },
      { type: "popVariance", name: "总体方差" },
      { type: "stdDeviation", name: "样本标准差" },
      { type: "popStandardDeviation", name: "总体标准差" },
    ];
    return {
      workerList: [], // 用来存储所有的线程
      calcList, // 计算类型
      arr, // 数据
      weightedList, // 加权因子
    };
  },
  methods: {
    makeWorker() {
      this.calcList.forEach((item) => {
        let workerName = `worker${this.workerList.length}`;
        let worker = new Worker();
        let start = performance.now();
        worker.postMessage({
          arr: this.arr,
          type: item.type,
          weightedList: this.weightedList,
        });
        worker.addEventListener("message", (e) => {
          worker.terminate();

          let tastName = "";
          this.calcList.forEach((item) => {
            if (item.type === e.data.type) {
              item.value = e.data.value;
              tastName = item.name;
            }
          });

          let end = performance.now();
          let duration = end - start;
          console.log(`当前任务: ${tastName}, 计算用时: ${duration} 毫秒`);
        });
        this.workerList.push({ [workerName]: worker });
      });
    },
    clearWorker() {
      if (this.workerList.length > 0) {
        this.workerList.forEach((item, key) => {
          item[`worker${key}`].terminate && item[`worker${key}`].terminate(); // 终止所有线程
        });
      }
    },
  },
  // 页面关闭,如果还没有计算完成,要销毁对应线程
  beforeDestroy() {
    this.clearWorker();
  },
};
</script>
import { create, all } from 'mathjs'
const config = {
  number: 'BigNumber',
  precision: 20 // 精度
}
const math = create(all, config);

//加
const numberAdd = (arg1,arg2) => {
  return math.number(math.add(math.bignumber(arg1), math.bignumber(arg2)));
}
//减
const numberSub = (arg1,arg2) => {
  return math.number(math.subtract(math.bignumber(arg1), math.bignumber(arg2)));
}
//乘
const numberMultiply = (arg1, arg2) => {
  return math.number(math.multiply(math.bignumber(arg1), math.bignumber(arg2)));
}
//除
const numberDivide = (arg1, arg2) => {
  return math.number(math.divide(math.bignumber(arg1), math.bignumber(arg2)));
}

// 数组总体标准差公式
const popVariance = (arr) => {
  return Math.sqrt(popStandardDeviation(arr))
}

// 数组总体方差公式
const popStandardDeviation = (arr) => {
  let s,
    ave,
    sum = 0,
    sums= 0,
    len = arr.length;
  for (let i = 0; i < len; i++) {
    sum = numberAdd(Number(arr[i]), sum);
  }
  ave = numberDivide(sum, len);
  for(let i = 0; i < len; i++) {
    sums = numberAdd(sums, numberMultiply(numberSub(Number(arr[i]), ave), numberSub(Number(arr[i]), ave)))
  }
  s = numberDivide(sums,len)
  return s;
}

// 数组加权公式
const weightedAverage = (arr1, arr2) => { // arr1: 计算列,arr2: 选择的权重列
  let s,
    sum = 0, // 分子的值
    sums= 0, // 分母的值
    len = arr1.length;
  for (let i = 0; i < len; i++) {
    sum = numberAdd(numberMultiply(Number(arr1[i]), Number(arr2[i])), sum);
    sums = numberAdd(Number(arr2[i]), sums);
  }
  s = numberDivide(sum,sums)
  return s;
}

// 数组样本方差公式
const variance = (arr) => {
  let s,
    ave,
    sum = 0,
    sums= 0,
    len = arr.length;
  for (let i = 0; i < len; i++) {
    sum = numberAdd(Number(arr[i]), sum);
  }
  ave = numberDivide(sum, len);
  for(let i = 0; i < len; i++) {
    sums = numberAdd(sums, numberMultiply(numberSub(Number(arr[i]), ave), numberSub(Number(arr[i]), ave)))
  }
  s = numberDivide(sums,(len-1))
  return s;
}

// 数组中位数
const middleNum = (arr) => {
  arr.sort((a,b) => a - b)
  if(arr.length%2 === 0){ //判断数字个数是奇数还是偶数
    return numberDivide(numberAdd(arr[arr.length/2-1], arr[arr.length/2]),2);//偶数个取中间两个数的平均数
  }else{
    return arr[(arr.length+1)/2-1];//奇数个取最中间那个数
  }
}

// 数组求和
const sum = (arr) => {
  let sum = 0, len = arr.length;
  for (let i = 0; i < len; i++) {
    sum = numberAdd(Number(arr[i]), sum);
  }
  return sum;
}

// 数组平均值
const average = (arr) => {
  return numberDivide(sum(arr), arr.length)
}

// 数组最大值
const max = (arr) => {
  let max = arr[0]
  for (let i = 0; i < arr.length; i++) {
    if(max < arr[i]) {
      max = arr[i]
    }
  }
  return max
}

// 数组最小值
const min = (arr) => {
  let min = arr[0]
  for (let i = 0; i < arr.length; i++) {
    if(min > arr[i]) {
      min = arr[i]
    }
  }
  return min
}

// 数组有效数据长度
const count = (arr) => {
  let remove = ['', ' ', null , undefined, '-']; // 排除无效的数据
  return arr.filter(item => !remove.includes(item)).length
}

// 数组样本标准差公式
const stdDeviation = (arr) => {
  return Math.sqrt(variance(arr))
}

// 数字三位加逗号,保留两位小数
const formatNumber = (num, pointNum = 2) => {
  if ((!num && num !== 0) || num == '-') return '--'
  let arr = (typeof num == 'string' ? parseFloat(num) : num).toFixed(pointNum).split('.')
  let intNum = arr[0].replace(/\d{1,3}(?=(\d{3})+(.\d*)?$)/g,'$&,')
  return arr[1] === undefined ? intNum : `${intNum}.${arr[1]}`
}

onmessage = function (e) {

  let {arr, type, weightedList} = e.data
  let value = '';
  switch (type) {
    case 'sum':
      value = formatNumber(sum(arr));
      break
    case 'average':
      value = formatNumber(average(arr));
      break
    case 'weightedAverage':
      value = formatNumber(weightedAverage(arr, weightedList));
      break
    case 'max':
      value = formatNumber(max(arr));
      break
    case 'middleNum':
      value = formatNumber(middleNum(arr));
      break
    case 'min':
      value = formatNumber(min(arr));
      break
    case 'variance':
      value = formatNumber(variance(arr));
      break
    case 'popVariance':
      value = formatNumber(popVariance(arr));
      break
    case 'stdDeviation':
      value = formatNumber(stdDeviation(arr));
      break
    case 'popStandardDeviation':
      value = formatNumber(popStandardDeviation(arr));
      break
    }

  // 发送数据事件
  postMessage({type, value});
}

总结

以上列子分了五个场景,直接引入项目就可以测试,最后一个例子请参考:一文彻底了解Web Worker,十万条数据都是弟弟附带源码。

写这篇文章为了巩固下webWorker的使用,希望能对你们有所帮助,如果有帮助请点个赞。谢了文章来源地址https://www.toymoban.com/news/detail-832507.html

到了这里,关于webWorker解决单线程中的一些小问题和性能优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android UI性能优化实战 识别绘制中的性能问题(1)

    { public String name; public int imageId; public String date; public String msg; public Droid(String msg, String date, int imageId, String name) { this.msg = msg; this.date = date; this.imageId = imageId; this.name = name; } public static List generateDatas() { List datas = new ArrayList(); datas.add(new Droid(“Lorem ipsum dolor sit amet, orci nullam cra”

    2024年04月13日
    浏览(47)
  • IDEA性能优化设置(解决卡顿问题)

            在我们日常使用IDEA进行开发时,可能会遇到许多卡顿的瞬间,明明我们的机器配置也不低啊?为什么就会一直卡顿呢?         原来这是因为IDEA软件在我们安装的时候就设置了默认的内存使用上限(通常很小),这就是造成我们使用IDEA时卡顿的根本原因。比

    2024年01月17日
    浏览(50)
  • IDEA性能优化设置(解决卡顿问题)修改内存

    在我们日常使用IDEA进行开发时,可能会遇到许多卡顿的瞬间,明明我们的机器配置也不低啊?为什么就会一直卡顿呢? 原来这是因为IDEA软件在我们安装的时候就设置了默认的内存使用上限(通常很小),这就是造成我们使用IDEA时卡顿的根本原因。比如我这台电脑,明明是

    2023年04月23日
    浏览(55)
  • 解决前端性能问题:如何优化大量数据渲染和复杂交互?

    ✨✨祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心!✨✨  🎈🎈作者主页: 喔的嘛呀🎈🎈 目录 引言 一、分页加载数据 二、虚拟滚动 三、懒加载 四、数据缓存 五、减少重绘和回流 六、优化图片和资源: 七、合并压缩文件 八、使用Web Workers  在前端开发

    2024年03月10日
    浏览(65)
  • 一些性能优化思路与策略

    今天公司同事做技术分享,题目就是:一些性能优化思路与策略,我学习了一下然后做了如下总结。 响应时间:平均响应时间,TP95、TP99等等。这里需要注意,响应时间有服务端响应时间和客户端响应时间的区别,一般关注服务端的相应时间。 吞吐量:QPS、TPS 系统负载:并发

    2024年02月16日
    浏览(50)
  • efcore的一些性能优化

    EF Core 提供了一种称为自动跟踪的功能,它可以将查询到的实体跟踪在内存中,以便对它们进行更改直到提交到数据库。在查询实体时,EF Core 将引用添加到内部跟踪集合中,以便可以在修改时进行检测和持久化到数据库中。 在上述示例中,我们在从数据库中查询到 Blog 实体

    2024年02月06日
    浏览(38)
  • 前端开发怎么解决性能优化的问题? - 易智编译EaseEditing

    前端性能优化是确保网站或应用在加载速度、响应性和用户体验等方面达到最佳状态的关键任务。以下是一些解决前端性能优化问题的方法: 压缩和合并代码: 压缩和合并CSS、JavaScript和HTML文件可以减少文件大小,加快加载速度。使用压缩工具(如UglifyJS和CSSNano)和构建工具

    2024年02月11日
    浏览(47)
  • vs2022的一些调试技巧——远程调试&线程检查&性能检查

    visual studio一直都是.net/c#开发人员最受欢迎的编译器,除了强大的代码提示和项目模板,还拥有大量的调试工具,这一期我们介绍下 code freeze 阶段的一些调试技巧。包括测试环境/生产环境下的远程调试,线程调试,以及性能监控调试。 目录 远程调试 1 安装 2.调试 线程调试

    2024年02月07日
    浏览(48)
  • 【HTML5高级第二篇】WebWorker多线程、EventSource事件推送、History历史操作

    1.1 概述 前端JS默认按照单线程去执行,一段时间内只能执行一件事情。举个栗子:比方说古代攻城游戏,带来十万大军,先让1000人去当炮灰(攻城),其他人就在后面看着等着,然后炮灰燃尽(这1000人攻城失败),然后第二批敢死队继续攻城,其他人还是等着…依次类推,

    2024年02月09日
    浏览(36)
  • Unity 之 一些资源标准、性能优化点整合整理

    目录 Unity 之 一些资源标准、性能优化点整合整理 零、总起 一、模型 二、图片 三、音频资源 四、灯光 五、碰撞体 六、font 七、UGUI 八、移动端性能优化心得 优化思路: 1)和美术指定好相关资源的规范 2)代码框架,合理的管理资源、多线程使用、对象池的使用、算法的优

    2024年02月05日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包