全文共2548字,預計學習時長8分鐘
多年以來,JavaScript最麻煩的特徵之一就是,如果某個任務耗時太長,剩下的代碼就會遇到阻塞而無法運行。
JavaScript是單線程的程式語言,這一特點導致用戶需要等待代碼按照順序運行。
但事實上有一種方式可以避免這種困境,它就是Worker。
在本文中,小芯將介紹Worker的使用方式。
閱讀之前
讀者需要知道JavaScript是一種單線程語言。
同步編碼
先來看看讀者可能已經熟知的JavaScript代碼樣式
let cnt = 0;
for (let i = 0; i < 10e8; i += 1) {
cnt += 1;
}
console.log(cnt);
在這段代碼的for循環中,cnt每次增加1,總共增加了次。而console.log要等到for循環結束後才能執行。
在Chrome瀏覽器控制臺中,這需要花費很長時間。
這段代碼需要耗費將近3秒。
無論如何,直到for循環結束cnt才會列印出來。
許多開發者都被這個問題困擾,因為用戶必須等待當前執行的任務完成。
異步代碼
JavaScript的設計者為開發者提供了不會阻塞程序流的特殊函數。這些函數處於和普通任務不同的等待隊列中。一般而言,它們可以在所有普通程序執行之後再執行。這些任務被稱為異步任務。
let cnt = 0;
setTimeout(() => {
for (let i = 0; i < 10e8; i += 1) {
cnt += 1;
}
console.log(cnt);
});
console.log(cnt);
這段代碼的運行結果和前面一段有所不同。
但仍然需要等待較長的時間。
這裡的for循環包含在 setTimeout中. setTimeout中的代碼會等待所有普通任務完成後再執行。
但這並非解決代碼流堵塞問題的最佳方案。雖然 setTimeout是一個不阻礙正常代碼流的異步函數,但是這樣做僅僅改變了函數的執行順序。
let cnt = 0;
setTimeout(() => {
for (let i = 0; i < 10e8; i += 1) {
cnt += 1;
}
console.log(cnt);
});
setTimeout(() => {
console.log(cnt);
});
console.log(cnt);
看看這個例子,其中使用了另一個setTimeout,它包含一個立即列印cnt的函數。然而第二個setTimeout總是在第一個setTimeout結束for循環後才開始運行console.log(cnt)指令。如果第一個setTimeout包含的任務耗時較長,那麼第二個setTimeout將無法運行。
為什麼?因為Javascript是一種單線程程式語言。異步函數存在於不同的任務隊列中,但它們仍然遵循單線程規則。
Workers
Web Worker是一種網絡接口,這意味著它無法訪問或管理文檔對象模型。Worker存在於一個不同的線程中,它和主線程互不幹擾。它在一個新的Worker對象創建時接受信息,然後向worker發送信息。
let worker;
if ('Worker' in window) {
worker = new Worker('file_name');
}
創建新的Worker實例十分簡單。new Worker這條指令接受一個字符串或者超連結作為參數。這個參數的格式一般如下所示:
new Worker('/worker.js');
實例創建之後,可以向另一個線程發送信息。
worker.postMessage('From Main Thread');
於是可以在worker所處的線程中收到這個信息。
// worker.jsthis.addEventListener('message', event => {
console.log(event.data);
});
Worker成功接受信息。
如果仔細查看日誌,會發現文件名是worker.js而不是main.js或者app.js。這說明worker接受了完好的信息。
現在,讓worker在收到主線程信息的同時也向主線程發送一條信息。
// main.js
worker.addEventListener('message', event => {
console.log(event.data);
});// worker.js
this.addEventListener('message', event => {
...
this.postMessage('From Worker Thread');
});
這段代碼看起來有些冗餘。讀者將會看到其工作流,但是首先來看看結果。
現在能看到兩條信息。
這段代碼是如何工作的呢?
onMessage代表從線程另一端接受信息這一事件,postMessage則代表向線程另一端發送信息這一事件。
代碼測試
在這個測試中可以看到兩點。
· For循環的運行總時長
· 代碼阻斷
這個示例使用React製作。狀態消息應該按照預期列印,然而同步和異步行為不會列印除了總運行時長外的任何信息。因為在React中,狀態改變也是一種異步行為,需要等待普通任務和其他前序異步任務完成才能執行。
另一方面,worker不需要等待,因為它位於另一個線程中。For循環在worker線程中,異步任務則在主線程中,所以它們不會互相打斷。
結論
通常worker被用於佔用大量CPU資源的程序中,比如2D canvas 和矢量圖。因為worker位於另一個線程中,它不會阻斷主線程中的任何任務,比如UI渲染。如果能將worker運用自如,它的效果將十分強大。包括IE10在內的眾多瀏覽器都能夠很好地支持這一功能。
留言點讚關注
我們一起分享AI學習與發展的乾貨
如轉載,請後臺留言,遵守轉載規範