webwoker是什么?

webwoker是在es5提出的,是在页面主线程外单独开一条线程用于处理复杂业务,防止主线程阻塞的技术。

首先由于浏览器是单线程浏览器单线程的原因是为了防止出现资源竞争的情况出现,所以在设计初期采用了单线程模式的原因。

但是单线程设计导致用户在访问浏览器过程中出现长时间任务,页面整体卡顿,导致用户体验差情况出现。

在es5之前当出现长任务,通常通过将长任务拆分成多个段任务去处理,或者通过其他的数据传输方式传输给前端。 es5以后我们可以通过webworker重新创建一条新的线程去处理相关任务。

场景:

今年7月做自己小项目时候出现,当后端返回百万级数据的时候前端接受数据,然后转换成相关文档耗时太长,导致整体页面体验感差,因此当时考虑在下载文件的过程中使用webworker来处理http请求的数据。

需求:

客户每月上传百万条excle数据,然后在下一个月需要将这些数据根据客户进行分批导出。

刚开始的解决方案是,后端查询上一个月所有数据,然后根据客户id进行聚合后将所有数据返回给前端,然后前端将拿到的数据进行拆分,按个进行导出。经过后期测试发现前端用户触发一次下载后一个http请求的时长有时候长达1min左右。

改进方案,先通过请求获取上一个月用户快递数量,然后在根据用户分批请求下载excle数据,最后使用webworker去处理这些请求,这样及时有某一些用户数据体量过大,也会在webworker中去处理,不会导致主线程阻塞。

 部分实现代码:

//vue.config.js中webworker配置
chainWebpack: config => {
    // set worker-loader
    config.module
      .rule('worker')
      .test(/.worker.js$/)
      .use('worker-loader')
      .loader('worker-loader')
      .end();
  },
// worker.js使用webworker
import axios from "axios";
self.onmessage = (params) => {
    console.log(88833, params.data);
    axios.defaults.headers.Authorization = params.data.token;
    const paramsTemp = {
        type: "all",
        all: true,
        name: params.data.name,
        fileName: params.data.fileName,
        timeStamp: params.data.timeStamp,
    }
    if (params.data.startTime) {
        paramsTemp.startTime = params.data.startTime;
    }
    if (params.data.endTime) {
        paramsTemp.endTime = params.data.endTime;
    }
    const length = params.data.data.length
    let startNum = 0;
    const getDataFun = function(num, length) {
        paramsTemp.userId = params.data.data[num]._id
        axios.get(params.data.url, {
            params: paramsTemp,
        }).then(res => {
            console.log('总共:', length)
            console.log('当前:', num)
            self.postMessage({
                type: 'continue',
                data: res.data
            })
            num++;
            if(num === length) {
                getAllData()
            } else {
                getDataFun(num, length)
            }
        })
    }
    getDataFun(startNum, length)

    const getAllData = function() {
        axios.get(params.data.url, {
            params: {
                type: 'countAll',
                timeStamp: params.data.timeStamp,
            },
        }).then(res => {
            self.postMessage({
                type: 'continue',
                data: res.data
            })
            self.postMessage({ type: 'close' }) 
        })
    }
}
// vue组件中使用worke.js并且进行数据通讯
	      import Worker from './worker.js';
	      this.worker = new Worker()
              const paramsTemp = {
                data: res.data,
                name: this.inputData,
                fileName: this.fileName,
                token: 'Bearer ' + localStorage.getItem('auth_token'),
                url: 'http://localhost:7001/api/download',
                timeStamp
              }
              if (this.value && this.value.length) {
                paramsTemp.startTime = this.value[0];
                paramsTemp.endTime = this.value[1];
              }
              this.worker.postMessage(paramsTemp)
              
              this.worker.addEventListener('message', (e) => {
                console.log('主线程监听到子线程发送的消息事件', e.data)
                if (e.data.type !== 'close') {
                  const dataAll = e.data.data;
                  dataAll.forEach((elt) => {
                    let buf;
                    if (typeof ArrayBuffer !== "undefined") {
                      buf = new ArrayBuffer(elt.data.length);
                      const view = new Uint8Array(buf);
                      for (let i = 0; i !== elt.data.length; ++i) {
                        view[i] = elt.data.charCodeAt(i) & 0xff;
                      }
                    } else {
                      buf = new Array(elt.data.length);
                      for (let i = 0; i !== elt.data.length; ++i) {
                        buf[i] = elt.data.charCodeAt(i) & 0xff;
                      }
                    }
                    const blob = new Blob([buf], { type: "application/octet-stream" });
                    FileSaver.saveAs(blob, elt.name + ".xlsx");
                  });
                } else {
                  this.worker.terminate()
                  this.loadingDown = false;
                  this.loading = false;
                }
              })
            }

posted on 2024-12-03 16:02  张小饭啊  阅读(72)  评论(0)    收藏  举报