摘要:異步編程通過(guò)在事件循環(huán)中調(diào)度要稍后執(zhí)行的部分代碼來(lái)使應(yīng)用程序能夠響應(yīng),從而允許首先執(zhí)行渲染。工作人員將通過(guò)定義的消息處理程序處理該消息。的應(yīng)用場(chǎng)景到目前為止,我們列出了的優(yōu)勢(shì)和局限性。
這一次我們將分拆Web Workers:我們將提供一個(gè)概述,討論不同類型的workers,他們的組成部分如何共同發(fā)揮作用,以及他們?cè)诓煌闆r下提供的優(yōu)勢(shì)和局限性。最后,我們將提供5個(gè)用例,其中Web Workers將是不錯(cuò)的選擇。
您應(yīng)該已經(jīng)熟悉JavaScript在單個(gè)線程上運(yùn)行的事實(shí),因?yàn)槲覀冎耙呀?jīng)詳細(xì)討論過(guò)它。但是,JavaScript也為開(kāi)發(fā)人員提供了編寫(xiě)異步代碼的機(jī)會(huì)。
異步編程的局限性我們之前已經(jīng)討論過(guò)異步編程,何時(shí)應(yīng)該使用它。
異步編程通過(guò)在事件循環(huán)中“調(diào)度”要稍后執(zhí)行的部分代碼來(lái)使應(yīng)用程序UI能夠響應(yīng),從而允許首先執(zhí)行UI渲染。
異步編程的一個(gè)很好的用例是制作AJAX請(qǐng)求。 由于請(qǐng)求可能需要很長(zhǎng)時(shí)間,因此可以異步制作請(qǐng)求,并且在客戶端等待響應(yīng)時(shí),可以執(zhí)行其他代碼。
// This is assuming that you"re using jQuery jQuery.ajax({ url: "https://api.example.com/endpoint", success: function(response) { // Code to be executed when a response arrives. } });
但是,這會(huì)帶來(lái)一個(gè)問(wèn)題 - 請(qǐng)求由瀏覽器的WEB API處理,但其他代碼如何可以異步? 例如,如果成功回調(diào)中的代碼是CPU密集型的:
var result = performCPUIntensiveCalculation();
如果performCPUIntensiveCalculation不是一個(gè)HTTP請(qǐng)求,而是一個(gè)阻塞代碼(例如一個(gè)巨大的for循環(huán)),則無(wú)法釋放事件循環(huán)并解除瀏覽器的用戶界面 - 它會(huì)凍結(jié)并對(duì)用戶無(wú)響應(yīng)。
這意味著異步函數(shù)僅解決JavaScript語(yǔ)言的單線程局限性的一小部分。
在某些情況下,通過(guò)使用setTimeout,您可以在從更長(zhǎng)時(shí)間運(yùn)行的計(jì)算中解除UI的情況下取得良好結(jié)果。例如,通過(guò)在獨(dú)立的setTimeout調(diào)用中對(duì)復(fù)雜的計(jì)算進(jìn)行批處理,您可以將它們置于事件循環(huán)中的多帶帶“位置”,這樣就可以獲得UI渲染/響應(yīng)性所需的時(shí)間。
我們來(lái)看看一個(gè)計(jì)算數(shù)值數(shù)組平均值的簡(jiǎn)單函數(shù):
function average(numbers) { var len = numbers.length, sum = 0, i; if (len === 0) { return 0; } for (i = 0; i < len; i++) { sum += numbers[i]; } return sum / len; }
這就是你如何重寫(xiě)上面的代碼并“模擬”異步性:
function averageAsync(numbers, callback) { var len = numbers.length, sum = 0; if (len === 0) { return 0; } function calculateSumAsync(i) { if (i < len) { // Put the next function call on the event loop. setTimeout(function() { sum += numbers[i]; calculateSumAsync(i + 1); }, 0); } else { // The end of the array is reached so we"re invoking the callback. callback(sum / len); } } calculateSumAsync(0); }
這將使用setTimeout函數(shù),該函數(shù)將在事件循環(huán)的更下方添加計(jì)算的每個(gè)步驟。 在每次計(jì)算之間,將有足夠的時(shí)間進(jìn)行其他計(jì)算,這是解凍瀏覽器所必需的。
Web Workers 扭轉(zhuǎn)局面HTML5給我們帶來(lái)了很多很棒的東西,包括:
SSE(我們已經(jīng)在之前的文章中描述并與WebSockets進(jìn)行了比較)
地理位置
應(yīng)用程序緩存
本地存儲(chǔ)
拖放
Web Worker
Web Workers是瀏覽器內(nèi)的線程,可用于執(zhí)行JavaScript代碼而不會(huì)阻止事件循環(huán)。
這真是太神奇了。 JavaScript的整個(gè)范例基于單線程環(huán)境的思想,但在這里來(lái)自網(wǎng)絡(luò)工作者(Web Workers),它解除了(部分)這種限制。
Web Workers允許開(kāi)發(fā)人員將長(zhǎng)時(shí)間運(yùn)行和計(jì)算密集型任務(wù)放在后臺(tái),而不會(huì)阻止用戶界面,從而使您的應(yīng)用程序更具響應(yīng)能力。更重要的是,為了解決事件循環(huán)的問(wèn)題,不需要使用setTimeout技巧。
下面是一個(gè)簡(jiǎn)單的演示,顯示了在有和沒(méi)有Web Workers的情況下對(duì)數(shù)組進(jìn)行排序的區(qū)別。
Web Worker概述Web Workers允許您執(zhí)行諸如啟動(dòng)長(zhǎng)時(shí)間運(yùn)行的腳本來(lái)處理計(jì)算密集型任務(wù),但不會(huì)阻止UI。實(shí)際上,這一切都是平行進(jìn)行的。Web Workers真正是多線程的。
你可能會(huì)說(shuō) - “JavaScript不是單線程語(yǔ)言嗎?”。
當(dāng)你意識(shí)到JavaScript是一種沒(méi)有定義線程模型的語(yǔ)言時(shí),這應(yīng)該是你的"aha!"時(shí)刻。 Web Workers不是JavaScript的一部分,它們是可以通過(guò)JavaScript訪問(wèn)的瀏覽器功能。大多數(shù)瀏覽器歷來(lái)都是單線程的(當(dāng)然,這已經(jīng)改變了),并且大多數(shù)JavaScript實(shí)現(xiàn)都發(fā)生在瀏覽器中。 Web Worker沒(méi)有在Node.JS中實(shí)現(xiàn) - 它有一個(gè)“cluster”或“child_process”的概念,有點(diǎn)不同。
值得注意的是,這個(gè)規(guī)范提到了三種類型的Web Workers:
Dedicated Worker
Shared Worker
Service Worker
Dedicated Worker專用Web Worker由主進(jìn)程實(shí)例化,并且只能與其進(jìn)行通信。
共享工作人員可以通過(guò)運(yùn)行在同一來(lái)源的所有進(jìn)程(不同的瀏覽器選項(xiàng)卡,iframe或其他共享工作人員)訪問(wèn)。
服務(wù)工作人員是一個(gè)事件驅(qū)動(dòng)的工作人員,針對(duì)原點(diǎn)和路徑進(jìn)行了注冊(cè)。 它可以控制與之關(guān)聯(lián)的網(wǎng)頁(yè)/網(wǎng)站,攔截并修改導(dǎo)航和資源請(qǐng)求,并以非常細(xì)化的方式緩存資源,從而使您可以很好地控制應(yīng)用在某些情況下的行為方式(例如,當(dāng)網(wǎng)絡(luò)不是可用。)
在這篇文章中,我們將關(guān)注Dedicated Workers(專職工作者)并將他們稱為“(Web Workders)網(wǎng)絡(luò)工作者”或“(Workers)工作者”。
Web Workders 如何工作Web Workers被實(shí)現(xiàn)為.js文件,這些文件通過(guò)頁(yè)面中的異步HTTP請(qǐng)求提供。 Web Worker API完全隱藏了這些請(qǐng)求。
工作人員利用類似線程的消息傳遞來(lái)實(shí)現(xiàn)并行性。 它們非常適合保持您的用戶界面的最新性,性能和響應(yīng)能力。
Web工作人員在瀏覽器中的獨(dú)立線程中運(yùn)行。 因此,它們執(zhí)行的代碼需要包含在多帶帶的文件中。 記住這一點(diǎn)非常重要。
讓我們看看如何創(chuàng)建一個(gè)基本的工作人員:
var worker = new Worker("task.js");
如果“task.js”文件存在且可訪問(wèn),瀏覽器將產(chǎn)生一個(gè)新的線程,以異步方式下載文件。 下載完成后,它將被執(zhí)行,工作人員將開(kāi)始工作。
如果提供的文件路徑返回404,工作人員將自動(dòng)失敗。
為了啟動(dòng)創(chuàng)建的worker,你需要調(diào)用postMessage方法:
worker.postMessage();網(wǎng)絡(luò)工作者通信
為了在Web Worker和創(chuàng)建它的頁(yè)面之間進(jìn)行通信,您需要使用postMessage方法或廣播頻道。
postMessage方法較新的瀏覽器支持將JSON對(duì)象作為該方法的第一個(gè)參數(shù),而舊版瀏覽器只支持一個(gè)字符串。
讓我們看一個(gè)創(chuàng)建worker的頁(yè)面如何與它進(jìn)行通信的例子,通過(guò)傳遞一個(gè)JSON對(duì)象作為一個(gè)更“復(fù)雜”的例子。 傳遞一個(gè)字符串是完全一樣的。
我們來(lái)看看下面的HTML頁(yè)面(或者更精確一些):
這就是我們的工作人員腳本的外觀:
self.addEventListener("message", function(e) { var data = e.data; switch (data.cmd) { case "average": var result = calculateAverage(data); // Some function that calculates the average from the numeric array. self.postMessage(result); break; default: self.postMessage("Unknown command"); } }, false);
點(diǎn)擊按鈕后,將從主頁(yè)面調(diào)用postMessage。 worker.postMessage行將JSON對(duì)象傳遞給worker,并添加cmd和數(shù)據(jù)鍵及其各自的值。 工作人員將通過(guò)定義的消息處理程序處理該消息。
當(dāng)消息到達(dá)時(shí),實(shí)際的計(jì)算正在工作者中執(zhí)行,而不會(huì)阻塞事件循環(huán)。 工作人員正在檢查傳遞的事件e并執(zhí)行,就像標(biāo)準(zhǔn)的JavaScript函數(shù)一樣。 完成后,結(jié)果會(huì)傳回主頁(yè)面。
在一名工人的背景下,自我和這一點(diǎn)都指向了工人的全球范圍。
有兩種方法可以阻止工作人員:通過(guò)從主頁(yè)面調(diào)用worker.terminate()或在worker本身內(nèi)部調(diào)用self.close()。
廣播頻道廣播頻道是更通用的通信API。 它允許我們將消息廣播到共享相同來(lái)源的所有上下文。 所有瀏覽器選項(xiàng)卡,iframe或從同一來(lái)源提供服務(wù)的工作人員都可以發(fā)送和接收消息:
// Connection to a broadcast channel var bc = new BroadcastChannel("test_channel"); // Example of sending of a simple message bc.postMessage("This is a test message."); // Example of a simple event handler that only // logs the message to the console bc.onmessage = function (e) { console.log(e.data); } // Disconnect the channel bc.close()
在視覺(jué)上,你可以看到廣播頻道的樣子,使其更加清晰:
雖然廣播頻道的瀏覽器支持有限,
消息的大小有兩種方式將消息發(fā)送給Web Workers:
復(fù)制消息:消息被序列化,復(fù)制,發(fā)送,然后在另一端解除序列化。頁(yè)面和工作人員不共享同一個(gè)實(shí)例,所以最終結(jié)果是每次傳遞都會(huì)創(chuàng)建一個(gè)副本。大多數(shù)瀏覽器通過(guò)自動(dòng)JSON編碼/解碼任何一端的值來(lái)實(shí)現(xiàn)此功能。正如所料,這些數(shù)據(jù)操作為消息傳輸增加了大量的開(kāi)銷(xiāo)。信息越大,發(fā)送時(shí)間越長(zhǎng)。
轉(zhuǎn)發(fā)郵件:這意味著原始發(fā)件人在發(fā)送后不能再使用它。傳輸數(shù)據(jù)幾乎是瞬間的。限制是只有ArrayBuffer可以轉(zhuǎn)讓。
Web Workers可用的功能由于Web Worker的多線程特性,Web Worker只能訪問(wèn)JavaScript特性的一個(gè)子集。以下是功能列表:
navigator對(duì)象
location對(duì)象(只讀)
XMLHttpRequest
setTimeout()/ clearTimeout()和setInterval()/ clearInterval()
應(yīng)用程序緩存
使用importScripts()導(dǎo)入外部腳本
創(chuàng)建其他Web Workers
Web Worker限制可悲的是,Web Workers沒(méi)有訪問(wèn)一些非常關(guān)鍵的JavaScript特性:
DOM(它不是線程安全的)
窗口對(duì)象
文檔對(duì)象
父對(duì)象
這意味著Web Worker不能操縱DOM(以及UI)。它有時(shí)可能會(huì)非常棘手,但是一旦您學(xué)會(huì)如何正確使用Web Workers,您將開(kāi)始將它們作為多帶帶的“計(jì)算機(jī)”使用,而所有UI更改將發(fā)生在您的頁(yè)面代碼中。工作人員將為您完成所有繁重的工作,一旦工作完成,您會(huì)將結(jié)果傳遞給對(duì)UI進(jìn)行必要更改的頁(yè)面。
處理錯(cuò)誤與任何JavaScript代碼一樣,您需要處理在Web Workers中引發(fā)的任何錯(cuò)誤。如果在執(zhí)行工作時(shí)發(fā)生錯(cuò)誤,則會(huì)觸發(fā)ErrorEvent。該接口包含三個(gè)有用的屬性,用于確定出錯(cuò)的地方:
filename - 導(dǎo)致錯(cuò)誤的工作者腳本的名稱
lineno - 發(fā)生錯(cuò)誤的行號(hào)
message - 錯(cuò)誤的描述
這是一個(gè)例子:
function onError(e) { console.log("Line: " + e.lineno); console.log("In: " + e.filename); console.log("Message: " + e.message); } var worker = new Worker("workerWithError.js"); worker.addEventListener("error", onError, false); worker.postMessage(); // Start worker without a message.
self.addEventListener("message", function(e) { postMessage(x * 2); // Intentional error. "x" is not defined. };
在這里,您可以看到我們創(chuàng)建了一geWorker并開(kāi)始監(jiān)聽(tīng)錯(cuò)誤事件。
在worker的內(nèi)部(在workerWithError.js中),我們通過(guò)將x乘以2來(lái)創(chuàng)建一個(gè)有意的異常,而未在該范圍中定義x。 異常傳播到初始腳本,并且正在調(diào)用有關(guān)錯(cuò)誤信息的onError。
Web Workers的應(yīng)用場(chǎng)景到目前為止,我們列出了Web Workers的優(yōu)勢(shì)和局限性?,F(xiàn)在讓我們看看最佳應(yīng)用場(chǎng)景:
光線追蹤:光線追蹤是一種通過(guò)將光線追蹤為像素來(lái)生成圖像的渲染技術(shù)。光線追蹤使用CPU密集型數(shù)學(xué)計(jì)算來(lái)模擬光線路徑。這個(gè)想法是模擬反射,折射,材質(zhì)等一些效果。所有這些計(jì)算邏輯都可以添加到Web Worker中以避免阻塞UI線程。更好的是 - 您可以輕松地將幾個(gè)工作人員(以及多個(gè)CPU之間)之間的圖像渲染分開(kāi)。這里是使用Web Workers進(jìn)行光線追蹤的簡(jiǎn)單演示 - https://nerget.com/rayjs-mt/r...。
加密:由于對(duì)個(gè)人和敏感數(shù)據(jù)的監(jiān)管日益嚴(yán)格,端到端加密越來(lái)越受歡迎。加密可能是一件非常耗時(shí)的事情,特別是如果有很多數(shù)據(jù)必須經(jīng)常加密(例如在將數(shù)據(jù)發(fā)送到服務(wù)器之前)。這是一個(gè)非常好的場(chǎng)景,可以使用Web Worker,因?yàn)樗恍枰L問(wèn)DOM或任何想象的東西 - 這是純粹的算法來(lái)完成他們的工作。一旦進(jìn)入工作人員,它對(duì)最終用戶而言是無(wú)縫的,并且不會(huì)影響他們的體驗(yàn)。
預(yù)取數(shù)據(jù):為了優(yōu)化您的網(wǎng)站或Web應(yīng)用程序并縮短數(shù)據(jù)加載時(shí)間,您可以利用Web Workers預(yù)先加載和存儲(chǔ)一些數(shù)據(jù),以便稍后在需要時(shí)使用它。在這種情況下,Web Workers是驚人的,因?yàn)樗鼈儾粫?huì)影響您的應(yīng)用的用戶界面,而不像工作時(shí)沒(méi)有工作人員那樣。
漸進(jìn)式Web應(yīng)用程序:即使網(wǎng)絡(luò)連接不穩(wěn)定,它們也必須快速加載。這意味著數(shù)據(jù)必須存儲(chǔ)在本地瀏覽器中。這是IndexDB或類似的API進(jìn)場(chǎng)的地方。基本上,需要客戶端存儲(chǔ)。為了在不阻塞UI線程的情況下使用,工作必須在Web Workers中完成。那么,就IndexDB而言,有一個(gè)異步API,即使沒(méi)有工作人員也可以執(zhí)行此操作,但之前有一個(gè)同步API(可能會(huì)再次引入),只能在工作人員中使用。
拼寫(xiě)檢查:基本拼寫(xiě)檢查程序按以下方式工作 - 程序讀取帶有拼寫(xiě)正確單詞列表的字典文件。正在將字典解析為搜索樹(shù)以使實(shí)際的文本搜索效率更高。當(dāng)一個(gè)單詞被提供給檢查器時(shí),程序檢查它是否存在于預(yù)先構(gòu)建的搜索樹(shù)中。如果在樹(shù)中沒(méi)有找到該單詞,則可以通過(guò)替代字符并測(cè)試它是否是有效的單詞 - 如果它是用戶想要寫(xiě)的單詞來(lái)為用戶提供替代拼寫(xiě)。所有這些處理都可以輕松卸載到Web Worker中,以便用戶可以在沒(méi)有任何UI阻塞的情況下鍵入單詞和句子,而工作人員則執(zhí)行所有搜索和提供建議。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/107760.html
摘要:最后,我們將會(huì)介紹個(gè)的使用場(chǎng)景。異步編程的局限性前面我們了解到異步編程及其使用時(shí)機(jī)。請(qǐng)求是一個(gè)很好的異步編程的使用場(chǎng)景。整個(gè)是基于單線程環(huán)境的而部分可以突破這方面的限制。最佳使用場(chǎng)景迄今為止,我們列舉了的長(zhǎng)處及其限制。 Web Workers 分類及 5 個(gè)使用場(chǎng)景 原文請(qǐng)查閱這里,略有刪減,本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 這是 JavaScri...
摘要:最后,提供個(gè)正確使用的場(chǎng)景。異步編程的一個(gè)很好的用例就請(qǐng)求。這意味著異步函數(shù)只能解決一小部分語(yǔ)言單線程中的局限性問(wèn)題。中有類似的集群子進(jìn)程概念,他們也是多線程但是和還是有區(qū)別。可用的特性由于的多線程特性,工作者只能訪問(wèn)特性的一個(gè)子集。 showImg(https://segmentfault.com/img/bVblS8J?w=400&h=298); 這是專門(mén)探索 JavaScript...
摘要:的生命周期的生命周期與頁(yè)面完全分離。換句話說(shuō),這個(gè)將為這個(gè)域中的所有內(nèi)容接收事件。這不是必要的,但絕對(duì)是推薦的。新的將啟動(dòng)并且安裝事件將被移除。使用,可以很容易被劫持連接并偽造響應(yīng)。后臺(tái)同步允許延遲操作,直到用戶具有穩(wěn)定的連接。 這是專門(mén)探索 JavaScript 及其所構(gòu)建的組件的系列文章的第8篇。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你! 如果你錯(cuò)過(guò)了前...
摘要:為了方便大家共同學(xué)習(xí),整理了之前博客系列的文章,目前已整理是如何工作這個(gè)系列,可以請(qǐng)猛戳博客查看。以下列出該系列目錄,歡迎點(diǎn)個(gè)星星,我將更友動(dòng)力整理理優(yōu)質(zhì)的文章,一起學(xué)習(xí)。 為了方便大家共同學(xué)習(xí),整理了之前博客系列的文章,目前已整理 JavaScript 是如何工作這個(gè)系列,可以請(qǐng)猛戳GitHub博客查看。 以下列出該系列目錄,歡迎點(diǎn)個(gè)星星,我將更友動(dòng)力整理理優(yōu)質(zhì)的文章,一起學(xué)習(xí)。 J...
閱讀 975·2022-06-21 15:13
閱讀 1857·2021-10-20 13:48
閱讀 1044·2021-09-22 15:47
閱讀 1376·2019-08-30 15:55
閱讀 3132·2019-08-30 15:53
閱讀 528·2019-08-29 12:33
閱讀 724·2019-08-28 18:15
閱讀 3471·2019-08-26 13:58