JS中webworker的作用與實操示例詳解
為什么需要webworker
JavaScript 需要引入 Web Worker 的核心原因源自其單線程模型的局限性。以下是關(guān)鍵原因及技術(shù)價值分析:
一、突破單線程阻塞瓶頸
1.JS 單線程的本質(zhì)
JavaScript 設(shè)計之初基于單線程模型,主線程需同時處理 UI 渲染、事件響應、邏輯計算等任務。若執(zhí)行耗時操作(如大數(shù)據(jù)計算),會導致頁面卡頓甚至無響應,用戶體驗急劇下降。Web 2.Worker 的解決方案
創(chuàng)建獨立的后臺線程運行腳本,將復雜任務剝離主線程,避免阻塞關(guān)鍵渲染流程
二、釋放多核 CPU 性能潛力
1.并行計算能力
Web Worker 允許創(chuàng)建多個子線程,將任務分片并行處理(如圖像濾鏡、科學計算),充分利用多核 CPU 硬件資源,顯著提升處理效率
2.資源分配優(yōu)化
主線程專注 UI 交互與渲染,Worker 線程處理計算密集型任務,實現(xiàn)計算資源合理分配
三、增強應用穩(wěn)定性與體驗
1.崩潰隔離機制
Worker 線程運行在獨立環(huán)境中,即使子線程崩潰或報錯,也不會影響主線程及其他 Worker 的穩(wěn)定性
2.流暢的用戶交互保障
例如實時金融圖表渲染場景,主線程僅負責接收結(jié)果并渲染,確保用戶操作無延遲
webworker的原理
Web Worker 是 JavaScript 實現(xiàn)多線程的核心機制,其原理涉及底層線程模型、通信機制和環(huán)境隔離等多個關(guān)鍵技術(shù)點。
核心工作機制詳解
1. 獨立線程模型
- Web Worker 在獨立操作系統(tǒng)線程中運行(非主線程)
- 瀏覽器為每個 Worker 分配:
- 獨立內(nèi)存堆??臻g
- 專屬 JavaScript 執(zhí)行環(huán)境
- 隔離的全局上下文 (
self代替window)
2. 線程間通信機制
// 主線程
const worker = new Worker('worker.js');
worker.postMessage(data); // 發(fā)送數(shù)據(jù)
// Worker 線程 (worker.js)
self.onmessage = (e) => {
const result = process(e.data); self.postMessage(result); // 返回結(jié)果
}3. 事件循環(huán)隔離
| 特性 | 主線程 | Web Worker |
|---|---|---|
| 事件循環(huán) | UI渲染+JS執(zhí)行 | 純JS執(zhí)行環(huán)境 |
| 阻塞影響 | 導致頁面凍結(jié) | 僅影響自身線程 |
| 任務優(yōu)先級 | 高(UI響應優(yōu)先) | 低(計算任務優(yōu)先) |
4. 資源訪問限制
Web Worker 無法訪問:
- DOM API (
document,window) - 父頁面變量/函數(shù)
- 部分BOM API (
alert,localStorage)
但可以訪問:
XMLHttpRequest/FetchsetTimeout/setIntervalWebSocketsIndexedDB
5. 線程生命周期管理
// 創(chuàng)建Worker
const worker = new Worker('worker.js');
// 終止Worker (立即停止)
worker.terminate();
// Worker內(nèi)部自終止
self.close();- 創(chuàng)建開銷:約5-20ms (取決于瀏覽器)
- 內(nèi)存獨立:Worker崩潰不影響主線程
- 自動回收:頁面關(guān)閉時自動銷毀
底層實現(xiàn)差異(瀏覽器引擎)
| 瀏覽器引擎 | 線程模型實現(xiàn) | 特點 |
|---|---|---|
| Chromium | Blink渲染引擎 + V8隔離實例 | 每個Worker獨立V8實例 |
| Firefox | SpiderMonkey獨立上下文 | 共享JIT編譯器但隔離堆棧 |
| Safari | JavaScriptCore獨立虛擬機 | 精細內(nèi)存管理 |
性能優(yōu)化要點
- 線程復用:避免頻繁創(chuàng)建/銷毀 (線程池模式)
- 批量通信:合并多次消息傳遞
- 高效數(shù)據(jù)結(jié)構(gòu):優(yōu)先使用TypedArray
- 負載均衡:根據(jù)CPU核心數(shù)動態(tài)分配任務
- 超時控制:防止僵尸線程
Web Worker 通過線程隔離和高效通信機制,在保持JavaScript單線程編程模型的同時,實現(xiàn)了真正的并行計算能力,成為現(xiàn)代Web應用性能優(yōu)化的關(guān)鍵基礎(chǔ)設(shè)施。
webworker典型應用場景
| 場景類型 | 實例 | 技術(shù)價值 |
|---|---|---|
| 大數(shù)據(jù)處理 | 實時日志分析/金融統(tǒng)計 | 避免遍歷海量數(shù)據(jù)阻塞 UI27 |
| 音視頻處理 | 4K 視頻解碼/Canvas 濾鏡 | 分離計算與渲染流程17 |
| 高頻交互優(yōu)化 | 游戲物理引擎/傳感器數(shù)據(jù)處理 | 減少主線程壓力25 |
| 預加載與緩存 | SPA 資源預載入 | 結(jié)合 Service Worker 提升加載速度 |
webworker實操
寫在前面:為什么主線程執(zhí)行大量的同步運算會導致頁面卡頓?
瀏覽器默認會有一個刷新頻率,這和個人電腦的默認刷新率有關(guān),一般默認為60HZ,也就是瀏覽器頁面會一秒鐘刷新60次,計算下來差不多每16ms就要刷新一次,才能保證頁面上所有的樣式和布局以及DOM節(jié)點信息是最新的,如果主線程此時運行大量運算,超過16ms,就會造成渲染阻塞。這就是需要用webworker分攤主線程大量運算的原因,至于每個webworker運行多久不需要我們擔心,因為它在其他線程上,每個worker都是一個新線程,它不在js主線程上,它的運行不會阻塞頁面渲染。
細節(jié)點補充:
1.如何查看自己電腦上的實際cpu核數(shù)與邏輯處理器核數(shù)
任務管理器>性能面板>內(nèi)核數(shù)與邏輯處理器數(shù)(一般邏輯處理器數(shù)會比真實內(nèi)核數(shù)要多)
2.電腦屏幕刷新率查看:
設(shè)置>系統(tǒng)>屏幕>高級顯示器設(shè)置>刷新率
因為webworker屬于web API,所以必須要運行在瀏覽器環(huán)境下,需要通過live-server插件運行示例html或者將相關(guān)代碼放置在一個vue或react模板工程中執(zhí)行
index.html內(nèi)容
<!doctype html>
<html lang="en">
<body>
<div id="app"></div>
<!-- 啟用webworker效果 -->
<script src="fiberMain.js"></script>
<!-- 不啟用webworker效果 -->
<!-- <script src="fiberWithout.js"></script> -->
</body>
</html>fiberMain.js
// 主線程代碼,引用fiberWorker.js作為worker線程執(zhí)行代碼,實現(xiàn)大量cpu密集型運算從主線程中拆分至worker線程中。
const startTime = Date.now();
// 動態(tài)啟用本機電腦上的cpu邏輯處理器核數(shù)并發(fā)執(zhí)行多個線程
const workCount = navigator.hardwareConcurrency || 4;
const fiberArr = Array(50).fill().map((_, index)=> 30 + index);
// 創(chuàng)建多個worker示例
const workers = Array(workCount).fill().map(() => new Worker('./fiberWorker.js'))
const chunkSize = Math.ceil(fiberArr.length / workCount);
// 任務切片分配給各個worker
workers.forEach((worker, i) => {
const start = i * chunkSize;
const chunk = fiberArr.slice(start, start + chunkSize);
worker.postMessage({chunk, id : i});
worker.onmessage = (e) => {
// 每個worker運算結(jié)束后worker的單獨耗時
console.log('每一個worker計算結(jié)果結(jié)束完成后經(jīng)過的毫秒數(shù)::', (Date.now() - startTime));
}
worker.onerror = function (err) {
console.error(err)
}
})
// 主線程代碼執(zhí)行到這里的耗時
console.log('主線程同步代碼執(zhí)行完成后的耗時毫秒數(shù):::', (Date.now() - startTime))fiberWorker.js worker線程所要執(zhí)行的邏輯代碼,需要和fibermain.js搭配使用
// worker計算邏輯,斐波那契遞歸數(shù)列計算
self.onmessage = (e) => {
const res = e.data.chunk.map(n => fibonacci(n))
self.postMessage({res, id: e.data.id})
}
// 斐波那契計算函數(shù)(模擬耗時操作)
function fibonacci(n) {
if (n <= 1) return n; // 基線條件:fib(0)=0, fib(1)=1
return fibonacci(n - 1) + fibonacci(n - 2); // 遞歸調(diào)用
}fiberWithout.js
// 無worker線程,大量的遞歸運算直接運行在主線程中的寫法,主線程同步執(zhí)行大量運算導致耗時很長,大概2000ms,會嚴重阻塞每16ms就要執(zhí)行一次的頁面渲染,導致頁面和用戶交互異常卡頓。
// 不用worker,主線程單線程計算
const startTime = Date.now();
const fiberArr = Array(22).fill().map((_, index)=> 20 + index);
function fibonacci(n) {
return n <= 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}
const res = fiberArr.map(n => fibonacci(n));
console.log('同步計算的最終結(jié)果::', res);
console.log('主線程同步計算的耗時毫秒數(shù):::', (Date.now() - startTime));fiberWithout.js 代碼在主線程上運行相同的斐波那契數(shù)列計算邏輯耗時2013ms
fiberMain.js 配合 fiberWorker.js 在主線程上運行相關(guān)代碼大大縮短同步代碼等待執(zhí)行時間,因為把大量的cpu密集型(遞歸回調(diào))運算放置在了多個worker中運算,所以主線程的同步代碼執(zhí)行很快,耗時2ms,但是這并不意味這worker內(nèi)單個線程中的運算很快速,實際每個worker運算耗時加著線程間的通信,單個worker最長執(zhí)行總耗時2064ms。
為什么要啟用worker,因為js主線程是單線程,js執(zhí)行與頁面渲染全在主線程上執(zhí)行,js同步代碼執(zhí)行過長會導致渲染阻塞,導致頁面卡頓,如果把上面的運算邏輯放在主線程中執(zhí)行(也就是fiberWithout.js這種寫法),那么會導致頁面有2000多ms的渲染阻塞,因為js主線程需要每16ms就要運行一次頁面渲染邏輯。所以會導致頁面卡頓,造成頁面渲染不及時以及用戶交互得不到及時響應。
把大量的運算分散到多個worker中,相當于把主線程的大量運算分攤到了其他線程中,縮減主線程同步代碼至2ms,不會造成頁面卡頓。至于每個worker運行2000ms那不是我們關(guān)心的重點,worker就是用來分攤主線程的運算負擔的,防止頁面卡頓。
webworker技術(shù)限制與注意事項
1.無法操作 DOM
Worker 線程無法訪問 window、document 等對象,需通過 postMessage 與主線程通信
2.通信成本控制
頻繁跨線程傳遞大數(shù)據(jù)(如圖像二進制流)可能抵消性能優(yōu)勢,需采用 Transferable Objects 優(yōu)化
// 高效傳遞 ArrayBuffer worker.postMessage(buffer, [buffer]);
總結(jié)
到此這篇關(guān)于JS中webworker的作用與實操的文章就介紹到這了,更多相關(guān)JS webworker詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用百度地圖JSAPI生成h7n9禽流感分布圖實現(xiàn)代碼
本文將詳細介紹下如何使用百度地圖JSAPI生成的H7N9感染分布圖,有對百度api感興趣的朋友可以參考下哈,希望可以幫助到你2013-04-04
Number.isInteger()判斷一個數(shù)值是否為整數(shù)報錯問題及解決
這篇文章主要介紹了Number.isInteger()判斷一個數(shù)值是否為整數(shù)報錯問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-05-05
JavaScript通過setTimeout實時顯示當前時間的方法
這篇文章主要介紹了JavaScript通過setTimeout實時顯示當前時間的方法,涉及javascript操作時間顯示的技巧,非常具有實用價值,需要的朋友可以參考下2015-04-04
javascript中createElement的兩種創(chuàng)建方式
這篇文章主要介紹了javascript中createElement的兩種創(chuàng)建方式,具有一定參考借鑒價值,需要的朋友可以參考下2015-05-05

