一文帶你掌握J(rèn)avaScript中worker的具體使用
什么是 JavaScript Worker
Worker 是在Web應(yīng)用程序中實(shí)現(xiàn)多線程的機(jī)制。它可以在一個(gè)單獨(dú)的線程中運(yùn)行腳本,獨(dú)立于主線程。
這樣,我們可以將一些耗時(shí)的計(jì)算任務(wù)交給Worker線程處理。以保證主線程的不會(huì)被阻塞。
為什么需要Worker
由于JavaScript是單線程的,所有任務(wù)在一個(gè)線程上執(zhí)行。如果遇到一個(gè)耗時(shí)的任務(wù)(比如大規(guī)模數(shù)據(jù)計(jì)算、圖像處理、復(fù)雜算法)。它會(huì)阻塞主線程導(dǎo)致頁(yè)面無(wú)法響應(yīng),用戶體驗(yàn)變差。
Worker的出現(xiàn)就是為了解決這個(gè)問(wèn)題,將耗時(shí)的計(jì)算任務(wù)放到后臺(tái)線程去執(zhí)行。
vue2中如何使用 worker
在vue/cli腳手架中,我們無(wú)法直接使用worker。
因?yàn)椋?/p>
1,無(wú)法將 Worker 腳本打包為獨(dú)立的可加載文件。
2,打包后會(huì)導(dǎo)致依賴丟失或路徑錯(cuò)誤。
npm install worker-loader -D
或者
yarn add worker-loader -D
vue.config.js 配置如下
module.exports = {
chainWebpack:config=>{
// 下面是worker的配置
.rule('worker')
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.end();
// 解決:worker 熱更新問(wèn)題
config.module.rule('js').exclude.add(/\.worker\.js$/);
},
parallel: false,
chainWebpack: config => {
// 解決:“window is undefined”報(bào)錯(cuò),這個(gè)是因?yàn)閣orker線程中不存在window對(duì)象,因此不能直接使用,要用this代替
config.output.globalObject('this')
}
}
postMessage 子線程給主線發(fā)送消息
self.postMessage子線程向主線程發(fā)送消息。在 Web Worker 中,self 指向當(dāng)前創(chuàng)建的這個(gè) worker 線程,self 是一個(gè)全局對(duì)象。
self.postMessage()將數(shù)據(jù)從 worker 線程發(fā)送到創(chuàng)建它的主線程。
主線程通知子線程(worker)開始工作
// 創(chuàng)建 worker
this.webWorker = new Worker('./web.worker.js')
// 通知 Worker 子線程可以進(jìn)行響應(yīng)的工作。
// 主線程調(diào)用worker.postMessage()方法,向 Worker 發(fā)消息
this.webWorker.postMessage({ action: 'startFetch' });
worker 發(fā)送網(wǎng)絡(luò)請(qǐng)求
src\views\element.vue 文件
<template>
<div class="page">
<h1>使用worker來(lái)發(fā)發(fā)送網(wǎng)絡(luò)請(qǐng)求</h1>
<el-button @click="sendFetchHandler">開始發(fā)送網(wǎng)絡(luò)請(qǐng)求</el-button>
<ul>
<li v-for="(item,index) in apiBackData" :key="index">{{ item.title }}</li>
</ul>
</div>
</template>
<script>
// 導(dǎo)入 worker 文件, 必須使用 這樣的方式引入
import WorkerA from 'worker-loader!./web.worker.js'
export default {
data() {
return {
apiBackData: []
}
},
methods: {
sendFetchHandler(){
// 主線程調(diào)用worker.postMessage()方法,向 Worker線程 發(fā)消息,開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求
this.webWorker1.postMessage({ action: 'startFetch' });
}
},
created() {
// 創(chuàng)建 worker 實(shí)例,名稱必須和導(dǎo)入時(shí)的保持一致
this.webWorker1 = new WorkerA('./web.worker.js')
// 先添加事件監(jiān)聽器
this.webWorker1.addEventListener('message', (e) => {
// e.data 是接收到的數(shù)據(jù)
console.log('收到來(lái)自 Worker 的消息:', e.data)
// 處理 Worker 返回的數(shù)據(jù)
if (e.data.type === 'fetchSuccess') {
this.apiBackData = e.data.data.data || []
console.log('獲取的數(shù)據(jù):', this.apiBackData );
} else if (e.data.type === 'fetchError') {
console.log('獲取數(shù)據(jù)失敗:', e.data.error);
}
})
this.webWorker1.addEventListener('error', (error) => {
console.error('Worker發(fā)生錯(cuò)誤:', error)
})
}
}
</script>
// 引入網(wǎng)絡(luò)請(qǐng)求中的方法
import {getList} from '@/request/api.js'
self.onmessage = function(e) {
console.log("worker:", e)
if(e.data && e.data.action==='startFetch'){
// e.data是主線程發(fā)送過(guò)來(lái)的數(shù)據(jù)
getList().then(res=>{
console.log(1111,res)
// 假設(shè) 是一個(gè)數(shù)組就是請(qǐng)求成功
if(Array.isArray(res)){
//將數(shù)據(jù)從 worker 線程發(fā)送到創(chuàng)建它的主線程
self.postMessage({
type: 'fetchSuccess',
data:{
data:res,
message: '獲取成功'
}
});
}else{
// 將數(shù)據(jù)從 worker 線程發(fā)送到創(chuàng)建它的主線程
self.postMessage({ type: 'fetchError', error: '請(qǐng)求失敗' });
}
})
}
};

worker導(dǎo)出表格案例
<template>
<div class="page">
<input type="text">
<el-button @click="exportExcelHandler">導(dǎo)出excel</el-button>
</div>
</template>
<script>
import {writeFile, utils} from 'xlsx'
export default {
data() {
return {
}
},
methods: {
exportExcelHandler(){
let arr = []
// 50w行的數(shù)據(jù)
for(let i= 0; i<500000;i++){
arr.push({
name: '張' + i,
age: 18,
sex: '男',
id: '2025_12_12'+ i,
address:'XX區(qū)YYY大道' + i + '號(hào)'
})
}
// 將對(duì)象數(shù)組 arr 轉(zhuǎn)換為 Excel 工作表(sheet)
const sheet = utils.json_to_sheet(arr)
// 創(chuàng)建一個(gè)新的空工作簿對(duì)象。
// 工作簿是 Excel 文件的容器,可以包含多個(gè)工作表
const workbook = utils.book_new()
// 將之前創(chuàng)建的工作表 sheet 添加到工作簿 workbook 中
utils.book_append_sheet(workbook, sheet, 'sheet1')
console.log('workbook', workbook)
// 將整個(gè)工作簿 workbook 寫入文件并觸發(fā)瀏覽器下載
writeFile(workbook, 'test.xlsx')
// 主線程調(diào)用worker.postMessage()方法,向 Worker線程 發(fā)消息,開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求
// this.webWorker1.postMessage({ action: 'startFetch', data: this.vueData });
}
}
}
</script>

worker解決添加數(shù)據(jù)耗時(shí)問(wèn)題
下載xlsx模塊。
npm install --save xlsx
使用worker的文件(下載的主頁(yè)面)
<template>
<div class="page">
<input type="text">
<el-button @click="exportExcelHandler">導(dǎo)出excel</el-button>
</div>
</template>
<script>
// 導(dǎo)入 worker 文件, 必須使用 這樣的方式引入
import WorkerA from 'worker-loader!./web.worker.js'
import {writeFile} from 'xlsx'
export default {
data() {
return {
}
},
methods: {
exportExcelHandler(){
// 主線程調(diào)用worker.postMessage()方法,向 Worker線程 發(fā)消息,開始進(jìn)行構(gòu)造數(shù)據(jù)
this.webWorker1.postMessage({ action: 'startCreateExcelData'});
}
},
created() {
// 創(chuàng)建 worker 實(shí)例,名稱必須和導(dǎo)入時(shí)的保持一致
this.webWorker1 = new WorkerA('./web.worker.js')
// 先添加事件監(jiān)聽器
this.webWorker1.addEventListener('message', (e) => {
console.log('收到來(lái)自 Worker 的消息:', e.data)
// 處理 Worker 返回的數(shù)據(jù)
if (e.data.type === 'createExcelSuccess') {
// 返回表的數(shù)據(jù)
const workbook = e.data.data.data
// 將整個(gè)工作簿 workbook 寫入文件并觸發(fā)瀏覽器下載。
// 最后異步仍然會(huì)卡頓,因?yàn)閿?shù)據(jù)量太大了.但是卡頓的時(shí)間會(huì)比之前好一些
writeFile(workbook, '通過(guò)worker導(dǎo)處excel.xlsx')
} else {
console.log('獲取數(shù)據(jù)失敗:', e.data.error);
}
})
this.webWorker1.addEventListener('error', (error) => {
console.error('Worker發(fā)生錯(cuò)誤:', error)
})
}
}
</script>
worker文件(src\views\web.worker.js )
// 引入 xlsx
import {utils} from 'xlsx'
self.onmessage = function(e) {
console.log("worker:", e)
if(e.data && e.data.action==='startCreateExcelData'){
// e.data是主線程發(fā)送過(guò)來(lái)的數(shù)據(jù)
let arr = []
// 50w行的數(shù)據(jù)
for(let i= 0; i<500000;i++){
arr.push({
name: '張' + i,
age: 18,
sex: '男',
id: '2025_12_12'+ i,
address:'XX區(qū)YYY大道' + i + '號(hào)'
})
}
// 將對(duì)象數(shù)組 arr 轉(zhuǎn)換為 Excel 工作表(sheet)
const sheet = utils.json_to_sheet(arr)
// 創(chuàng)建一個(gè)新的空工作簿(book)對(duì)象。工作簿是 Excel 文件的容器,可以包含多個(gè)工作表(sheet)
const workbook = utils.book_new()
// 將之前創(chuàng)建的工作表 (sheet) 添加到工作簿(workbook)中
utils.book_append_sheet(workbook, sheet, 'sheet1')
self.postMessage({
type: 'createExcelSuccess',
data:{
data:workbook,
message: '獲取成功'
}
});
}
};

關(guān)閉 Web Worker
在主線程和在Worker線程的關(guān)閉方式是不一樣的。如果在主線程,調(diào)用terminate()方法關(guān)閉
this.webWorker1 = new WorkerA('./web.worker.js')
this.webWorker1.terminate();
如果是在Work線程,可以調(diào)用close()方法進(jìn)行關(guān)閉
self.close();
主線程關(guān)閉 worker 線程的說(shuō)明
當(dāng)主線程調(diào)用worker.terminate()時(shí),Worker線程會(huì)被立即終止。
無(wú)論當(dāng)前事件循環(huán)中是否有任務(wù),包括微任務(wù)和宏任務(wù),都不會(huì)繼續(xù)執(zhí)行。
也就是說(shuō):如果我在點(diǎn)擊[導(dǎo)入excel]按鈕后,馬上又關(guān)閉點(diǎn)擊[關(guān)閉worker],那么不能夠正常導(dǎo)出表格。

Worker線程內(nèi)部關(guān)閉 worker 線程的說(shuō)明
當(dāng)Worker線程內(nèi)部調(diào)用self.close()時(shí),Worker線程并不會(huì)立即終止。
它會(huì)[繼續(xù)執(zhí)行當(dāng)前]事件循環(huán)中的任務(wù),但是[會(huì)阻止后續(xù)]事件循環(huán)的任務(wù)執(zhí)行。
也就是說(shuō):如果我在點(diǎn)擊[導(dǎo)入excel]按鈕后,馬上又關(guān)閉點(diǎn)擊[Worker內(nèi)部關(guān)閉Worker],仍然可以導(dǎo)出表格。

判斷用戶瀏覽器是否支持 worker
if (typeof Worker !== 'undefined') {
console.log('瀏覽器支持 Web Workers');
} else {
console.log('瀏覽器不支持 Web Workers');
}
Worker 線程引用其他js文件
有些時(shí)候,我們需要在worker文件中,引入其他模塊的文件。
可以通過(guò) importScripts() 方法來(lái)實(shí)現(xiàn),在 worker 中導(dǎo)入其他的模塊。這個(gè)地址是可以跨域的。
Worker 指定模塊類型
如果我們發(fā)現(xiàn)我們需要導(dǎo)出的方法使用的是ESModule 模式導(dǎo)出。
此時(shí)使用 importScripts() 方法引入他模塊,會(huì)報(bào)錯(cuò)。
此時(shí)需要指定模塊類型。
// main.js(主線程的代碼)
const worker = new Worker('/worker.js', {
type: 'module' // 指定 worker.js 的類型
});
// worker.js(worker線程的代碼)
import add from './utils.js'; // 導(dǎo)入外部js
self.addEventListener('message', e => {
postMessage(e.data);
});
add(1, 2);
// utils.js 使用了ESModule 模塊導(dǎo)出的 add 方法 export default add = (a, b) => a + b;
使用worker的注意事項(xiàng)
1,worker不能使用window上的dom操作,也不能獲取dom對(duì)象,dom相關(guān)的東西只有主線程有。worker只能做一些計(jì)算相關(guān)的操作
2,有的東西是無(wú)法通過(guò)主線程傳遞個(gè)子線程的。
比如:方法,dom節(jié)點(diǎn),一些對(duì)象里的特殊設(shè)置(freeze,getter,setter這些)所以vue的響應(yīng)式對(duì)象在傳遞之后就會(huì)變成普通對(duì)象
worker實(shí)際應(yīng)用案例
1,將語(yǔ)法檢查、代碼壓縮等任務(wù)放在 Worker 中執(zhí)行
2,圖像處理:比如使用Canvas處理圖片,進(jìn)行濾鏡
worker性能優(yōu)化建議
減少通信開銷:避免頻繁傳遞大量數(shù)據(jù),必要時(shí)使用 Transferable Objects(如 ArrayBuffer)
批量處理:對(duì)于大批量任務(wù),可批量創(chuàng)建 Worker 并行處理
常駐線程:對(duì)于頻繁使用的 Worker,可保持常駐而非重復(fù)創(chuàng)建
錯(cuò)誤處理:始終監(jiān)聽 onerror 事件,捕獲 Worker 內(nèi)部的異常
以上就是一文帶你掌握J(rèn)avaScript中worker的具體使用的詳細(xì)內(nèi)容,更多關(guān)于JavaScript worker用法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript如何將時(shí)間戳轉(zhuǎn)化為年月日時(shí)分秒格式
這篇文章主要給大家介紹了關(guān)于JavaScript如何將時(shí)間戳轉(zhuǎn)化為年月日時(shí)分秒格式的相關(guān)資料,在前端的日常工作當(dāng)中,時(shí)間戳的使用也是不少的,有時(shí)后端返回給我們的數(shù)據(jù)是一個(gè)時(shí)間戳,我們需要轉(zhuǎn)換成年月日,時(shí)分秒的形式展示在頁(yè)面當(dāng)中,需要的朋友可以參考下2023-11-11
基于BootStrap Metronic開發(fā)框架經(jīng)驗(yàn)小結(jié)【五】Bootstrap File Input文件上傳插件的用法
本文主要基于我自己的框架代碼案例,介紹其中文件上傳插件File Input的使用,非常具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-05-05
前端實(shí)現(xiàn)帶滾動(dòng)區(qū)域的DOM長(zhǎng)截圖導(dǎo)出完整步驟
前端實(shí)現(xiàn)截圖功能有多種方式,開發(fā)中實(shí)現(xiàn)截圖也是常見的功能需求,這篇文章主要介紹了前端實(shí)現(xiàn)帶滾動(dòng)區(qū)域的DOM長(zhǎng)截圖導(dǎo)出的相關(guān)資料,文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下2026-02-02
JavaScript生成驗(yàn)證碼并實(shí)現(xiàn)驗(yàn)證功能
這篇文章主要介紹了JavaScript生成驗(yàn)證碼并實(shí)現(xiàn)驗(yàn)證功能的相關(guān)資料,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
JS在TextArea光標(biāo)位置插入文字并實(shí)現(xiàn)移動(dòng)光標(biāo)到文字末尾
JS在TextArea光標(biāo)位置插入文字+移動(dòng)光標(biāo)到文字末尾,F(xiàn)irefox,Chrome,Safari以及Opera都有selectionStart和selectionEnd屬性,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下哈2013-06-06
js實(shí)現(xiàn)的捐贈(zèng)管理完整實(shí)例
這篇文章主要介紹了js實(shí)現(xiàn)的捐贈(zèng)管理完整實(shí)例,包括了html頁(yè)面、js腳本及css樣式的完整實(shí)現(xiàn)代碼,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-01-01
js發(fā)送短信倒計(jì)時(shí)的簡(jiǎn)單實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇js發(fā)送短信倒計(jì)時(shí)的簡(jiǎn)單實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09
基于JavaScript實(shí)現(xiàn)游戲購(gòu)物車效果詳解
這篇文章主要介紹了如何利用JavaScript實(shí)現(xiàn)游戲購(gòu)物車效果,文中的實(shí)現(xiàn)代碼講解詳細(xì),對(duì)我們學(xué)習(xí)JavaScript有一定幫助,需要的可以參考一下2022-03-03
avaScript基礎(chǔ)學(xué)習(xí)-基本的語(yǔ)法規(guī)則
這篇文章主要介紹了avaScript的語(yǔ)法規(guī)則,本文的語(yǔ)法講解主要講一下與其他語(yǔ)言的區(qū)別,下面詳細(xì)的介紹內(nèi)容,需要的小伙伴可以參考一下,希望對(duì)你有所幫助2022-02-02

