前端web worker的使用
技術標籤:javascript
JavaScript是單執行緒的程式語言,當遇到需要處理大量資料的邏輯計算時需要等待程式碼按照順序執行,這會導致使用者需要等待這段程式碼執行完後才能對頁面進行操作(UI 互動)嚴重的可能會前端頁面卡死的情況發生。
但有一種方式可以避免這種困境,它就是web worker。
web worker 的作用,就是為 JavaScript 創造多執行緒環境,worker存在於一個不同的執行緒中,它和主執行緒互不干擾,這樣就可以把處理大量資料的邏輯計算放在worker裡,主執行緒(通常負責 UI 互動)就會很流暢,不會被阻塞或拖慢。
Worker 執行緒一旦新建成功,就會始終執行,不會被主執行緒上的活動(比如使用者點選按鈕、提交表單)打斷。這樣有利於隨時響應主執行緒的通訊。但是,這也造成了 Worker 比較耗費資源,不應該過度使用,而且一旦使用完畢,就應該關閉。
一、使用web worker 需要注意以下幾個點。
(1)同源限制
分配給 Worker 執行緒執行的指令碼檔案,必須與主執行緒的指令碼檔案同源,如果js檔案是在cdn服務上託管的可以把js檔案轉成字串。
(2)web worker上下文
worker中的上下文和主執行緒js的上下文物件是不同的,window不是它的頂層物件,所以window相關的一些方法如alert等時不能使用的,還有dom也是不能訪問的。不過基本的方法。例如console.log、navigator、location、setTimeout等可以訪問。
(3)通訊聯絡
正如前面提到的,web worker誕生的目的在於解決耗時操作對ui互動的影響,worker 執行緒和主執行緒不在同一個上下文環境,它們不能直接通訊,必須通過訊息完成。
worker 執行緒無法讀取本地檔案,即不能開啟本機的檔案系統(file://),它所載入的指令碼,必須來自網路。
(5) worker使用node_modules中的依賴
worker如果需要使用node_modules中的依賴,可以使用worker-loader或workerize-loader來處理。
二、worker的基本使用
主執行緒js
// 引入worker指令碼檔案
const worker = new Worker('worker.js');
// 監聽message事件
worker.addEventListener('message', (event) => {
console.log(event.data);
});
const data = {
title: '有大量資料需要處理',
num: 60,
}
/*
* worker.postMessage()方法的引數,就是主執行緒傳給 Worker 的資料。它可以是各種資料型別,包括二進位制資料。
/*
// 傳送訊息 //轉成JSON字串
worker.postMessage(JSON.stringify(data));
worker.js
// worker檔案
self.addEventListener('message', (event) => {
// 獲取主執行緒傳送過來的訊息
console.log(event.data);
const { num, title } = JSON.parse(event.data);
console.log(title); //有大量資料需要處理
//遞迴
function fn(n) {
if (n === 1 || n === 2) {
return 1;
}
return fn(n - 1) + fn(n -2);
}
const newNum = fn(num);
const newData = {
title: '已處理完畢',
num: newNum
}
// 處理完畢傳送到主執行緒
self.postMessage(JSON.stringify(newData));
})
worker 載入指令碼
importScripts('script1.js');
//同時載入多個指令碼
importScripts('script1.js', 'script2.js');
worker 錯誤處理
主執行緒可以監聽 worker 是否發生錯誤。如果發生錯誤,worker 會觸發主執行緒的error事件。worker 內部也可以監聽error事件。
worker.onerror((event) => {
console.log(event)
});
// 或者
worker.addEventListener('error', (event) => {
console.log(event)
});
為了節省系統資源可以使用完worker後關閉worker
// 主執行緒
worker.terminate();
// Worker 執行緒
self.close();
三、在worker中使用node_modules依賴
在worker中使用node_modules依賴, 這裡我個人推薦使用workerize-loader
workerize-loader
將模組及其依賴項移動到 web 輔助角色的 web 包載入程式,自動將匯出的函式反映為非同步代理。
- 將一個小型、專門構建的 RPC 實現捆綁到應用中
- 如果匯出的模組方法已非同步,則簽名保持不變
- 支援同步和非同步工作函式
- 與非同步/等待效果優美
- 匯入的值是可例項化的,只是一個裝飾Worker
安裝
npm install -D workerize-loader
使用
worker.js
// 引入常用的js庫
import moment from 'moment';
import _ from 'lodash';
self.onmessage = (e) => {
console.log(moment);
console.log(_);
}
主執行緒
import Worker from 'workerize-loader!./worker';
const worker = new Worker();
worker.addEventListener('message', e => {
console.log(e)
});
IE11 需要多填充
workerize 載入程式支援支援 web worker 的瀏覽器 即 IE10+。但是,這些瀏覽器需要多填充才能使用"承諾",而"工人載入器"所依賴的承諾。建議在全球範圍內安裝多填充,因為 Webpack 本身也需要承諾載入捆綁包。
最小的實現是我們建議安裝的實現:
npm i promise-polyfill
然後,在模組中,您正在"工作",只需將其新增為您的第一次匯入:
import 'promise-polyfill/src/polyfill';
所有工作程式碼現在都可以使用承諾。
workerize-loader更多使用參考