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-loaderthis.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.dataconst xhr = new XMLHttpRequest()xhr.open(method, url, true)xhr.responseType = responseTypexhr.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.datalet value = '';switch (type) {case 'sum':value = formatNumber(sum(arr));breakcase 'average':value = formatNumber(average(arr));breakcase 'weightedAverage':value = formatNumber(weightedAverage(arr, weightedList));breakcase 'max':value = formatNumber(max(arr));breakcase 'middleNum':value = formatNumber(middleNum(arr));breakcase 'min':value = formatNumber(min(arr));breakcase 'variance':value = formatNumber(variance(arr));breakcase 'popVariance':value = formatNumber(popVariance(arr));breakcase 'stdDeviation':value = formatNumber(stdDeviation(arr));breakcase 'popStandardDeviation':value = formatNumber(popStandardDeviation(arr));break}// 發送數據事件postMessage({type, value});
}

總結

以上列子分了五個場景,直接引入項目就可以測試,最后一個例子請參考:一文徹底了解Web Worker,十萬條數據都是弟弟附帶源碼。

寫這篇文章為了鞏固下webWorker的使用,希望能對你們有所幫助,如果有幫助請點個贊。謝了

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/207738.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/207738.shtml
英文地址,請注明出處:http://en.pswp.cn/news/207738.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

全局平均池化的示例

1.對一個3通道&#xff0c;5*5的矩陣&#xff0c;進行全局平均池化 每個矩陣的大小都是 5x5&#xff0c;假設這些矩陣代表一幅圖像的三個不同通道。為簡單起見&#xff0c;我們將這三個矩陣分別稱為 A、B 和 C。合成圖像將是一個三通道圖像&#xff0c;每個通道由其中一個矩陣…

計算機方向的一些重要縮寫和簡介

參考&#xff1a; 深度學習四大類網絡模型 干貨|機器學習超全綜述&#xff01; 機器學習ML、卷積神經網絡CNN、循環神經網絡RNN、馬爾可夫蒙特卡羅MCMC、生成對抗網絡GAN、圖神經網絡GNN——人工智能經典算法 MLP&#xff08;Multi Layer Perseption&#xff09;用在神經網絡中…

這是最后的戰役了

不變因子 初等因子 行列式因子 smith標準型 酉矩陣 H-陣等等 A H A A^H A AHA 就是 H-陣 正定H陣的性質 若 A A A 為正定的H-陣. 存在可逆矩陣 Q Q Q&#xff0c; 使得 A Q H Q AQ^H Q AQHQ.存在 P P P, 使得 P H A P I P^HAPI PHAPI.A的特征值大于0. Q ? 1 A Q Q^{…

駕馭蘋果的人工智慧模式:克服反擊與應對挑戰

蘋果一年一度的秋季「春晚」時間越來越近&#xff0c;但在大模型浪潮下&#xff0c;蘋果何時推出自己的「蘋果GPT」成了另一個關注的話題。 畢竟&#xff0c;前有華為&#xff0c;后有小米&#xff0c;在中國手機廠商爭相將大模型裝進移動終端的同時&#xff0c;蘋果卻依舊對A…

微服務學習:Ribbon實現客戶端負載均衡,將請求分發到多個服務提供者

Ribbon是Netflix開源的一個基于HTTP和TCP客戶端負載均衡器。它主要用于在微服務架構中實現客戶端負載均衡&#xff0c;將請求分發到多個服務提供者上&#xff0c;從而實現高可用性和擴展性。 Ribbon的主要特點包括&#xff1a; 客戶端負載均衡&#xff1a;Ribbon是一個客戶端負…

【算法題】找出符合要求的字符串子串(js)

題解&#xff1a; function solution(str1, str2) {const set1 new Set([...str1]);const set2 new Set([...str2]);return [...set1].filter((item) > set2.has(item)).sort();}console.log(solution("fach", "bbaaccedfg"));//輸入:fach// bbaacced…

手機上寫工作總結用什么軟件好?借助工作筆記輕松寫出優秀年終總結

隨著年底的臨近&#xff0c;撰寫個人年終工作總結成為了許多職場人士的重要任務。因為手機是每個上班族都要隨身攜帶的電子設備&#xff0c;所以想要抽時間來寫年終工作總結&#xff0c;使用手機是比較便捷的。那么&#xff0c;在手機上寫工作總結應該使用什么軟件呢&#xff1…

Linux 環境下的性能測試——top與stress

對于Linux 環境&#xff0c;top命令是使用頻繁且信息較全的命令&#xff0c; 它對于所有正在運行的進行和系統負荷提供實時更新的概覽信息。stress是個簡單且全面的性能測試工具。通過它可以模擬各種高負載情況。 通過top與stress這兩個命令的結合使用&#xff0c;基本可以達到…

軟件測試——單元測試

單元測試是軟件開發中的一種測試方法&#xff0c;用于驗證軟件中的各個獨立單元&#xff08;通常是函數、方法或類&#xff09;是否按照設計規范正常工作。以下是進行單元測試的一般步驟和最佳實踐&#xff1a; 1. 選擇測試框架 選擇適合項目的測試框架&#xff0c;例如&…

SHAP:Python的可解釋機器學習庫

SHAP:Python的可解釋機器學習庫 一、概念二、步驟三、代碼-以波士頓房價為例summary_plotFeature Importanceshap_interaction_valuesdependence_plot完整代碼一、概念 SHAP(Shapley Additive Explanations)模型是一種用于解釋機器學習模型預測結果的方法。它基于合作博弈論…

【C++】類和對象——explicit關鍵字,友元和內部類

這篇博客已經到了類和對象的最后一部分了&#xff0c;下面我們先看一下explicit關鍵字 我們還是先來引入一個例子&#xff0c;我們的代碼是可以這么寫的 class A { public:A(int aa 0) {_a aa;cout << "A(int aa 0)" << endl;} private:int _a; }; i…

紅隊攻防實戰之Redis-RCE集錦

心若有所向往&#xff0c;何懼道阻且長 Redis寫入SSH公鑰實現RCE 之前進行端口掃描時發現該機器開著6379&#xff0c;嘗試Redis弱口令或未授權訪問 嘗試進行連接Redis&#xff0c;連接成功&#xff0c;存在未授權訪問 嘗試寫入SSH公鑰 設置redis的備份路徑 設置保存文件名 …

[GXYCTF2019]禁止套娃1

提示 git泄露無參數rce &#xff01;&#xff01;注意需要python3環境 github里dirsearch工具下載位置 ###可能需要開節點才能打開 百度網盤dirsearch下載地址 ###如果github里下載不了可以在網盤下載 提取碼sx5d 只給了flag在哪里呢&#xff0c;那么應該就是要讓…

Java參數校驗詳解:使用@Valid注解和自定義注解進行參數驗證

很多時候我們需要使用不少if、else等等邏輯判斷及驗證&#xff0c;這樣在進行一些重復的參數校驗會很麻煩&#xff0c;且以后要維護也會吃力。 而這樣就可以使用javax.validation。驗證&#xff08;Validation&#xff09;常見的驗證操作包括驗證數據的類型、格式、長度、范圍、…

我想漲工資,請問測試開發該怎么入門?

我是測試開發工程師!歡迎和我交流測試領域相關問題&#xff08;測試入門、技術、python交流都可以&#xff09; 我幾乎是靠這套方法&#xff0c;從一個只會功能測試的小白&#xff0c;到成為測試開發工程師的。 別急&#xff0c;先慢慢看&#xff0c;只要按照下面的流程走一遍…

postcss-pxtorem實現頁面自適應的原理

先聲明一點這玩意本身不能實現哈&#xff0c;他只是一個工具&#xff0c;更是一個postcss的插件 幫助我們從px轉化成為rem比如我們的代碼 div {height: 100px;width: 100px; }經過這個插件轉化之后變成 假設變成下面這樣哈 div {height: 1rem;width: 1rem; }其他沒啥子太大作…

P1046 [NOIP2005 普及組] 陶陶摘蘋果題解

題目 陶陶家的院子里有一棵蘋果樹&#xff0c;每到秋天樹上就會結出 1010 個蘋果。蘋果成熟的時候&#xff0c;陶陶就會跑去摘蘋果。陶陶有個 3030 厘米高的板凳&#xff0c;當她不能直接用手摘到蘋果的時候&#xff0c;就會踩到板凳上再試試。 現在已知 1010 個蘋果到地面的…

Spring全面詳解

目錄 1. Spring 概述 1.1 Spring是什么 1.2 Spring的作用 1.3 Spring IoC是什么 2. Spring 快速入門 3. Spring Bean 3.1 的實例化方式 空參構造器 3.2 的屬性注入 全參構造器注入 setter方法注入 策略模式 3.3 注解管理 3.4 注解方式的屬性注入 1. Spring 概述 …

奕碳科技亮相COP28:展現中國智慧,引領全球碳減排新篇章

11月30日,聯合國氣候變化框架公約第28次締約方大會 (COP28) 在阿聯酋迪拜開幕。COP28是全球氣候治理的重要盛會&#xff0c;匯聚了世界各國領導人、企業界和科學界代表&#xff0c;共同探討和制定應對全球氣候變化的策略與行動計劃。在這樣的背景下&#xff0c;企業群體的積極參…

跨境電商系統搭建 無貨源系統對接API 反向代購系統

跨境電商系統是為國外的客戶代購中國商品的系統&#xff0c;系統實現與國內電商API對接&#xff0c;客戶可直接在系統中搜索國內電商平臺的商品。查看演示網站 一級功能二級功能 標準版 高級版 企業版 企業旗艦版 大客戶尊享版 標準版高級版企業版企業旗艦版大客戶尊享版 前臺主…