最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入理解JavaScript中的并行處理

 更新時間:2016年09月22日 11:17:24   投稿:daisy  
這篇文章給大家詳細介紹了JavaScript中的并行處理,一直以來,JavaScript 都并沒有多線程的能力,而單線程在讓開發(fā)者方便的同時,也使它一直不能處理計算量復(fù)雜的場景,唯一方法就是讓服務(wù)端去做。下來通過這篇文章我們一起來看看詳細介紹。

前言

為什么說多線程如此重要?這是個值得思考的問題。一直以來,派生線程以一種優(yōu)雅的方式實現(xiàn)了對同一個進程中任務(wù)的劃分。操作系統(tǒng)負責(zé)分配每個線程的時間片,具有高優(yōu)先級并且任務(wù)繁重的線程將分配到更多的時間片,而低優(yōu)先級空閑的線程只能分到較少的時間片。

雖然多線程如此重要,但JavaScript卻并沒有多線程的能力。幸運的是,隨著 Web Worker 的普及,我們終于可以在后臺線程來處理資源密集型的計算了。而不好的方面是,目前制定的標準只適用于當(dāng)前的生態(tài)系統(tǒng),這有時候就比較尷尬了。如果你了解其他從一開始就支持多線程的語言的話,你可能會發(fā)現(xiàn)很多的限制,遠非僅僅是實例化一個新線程,然后你操控這個實例就能實現(xiàn)多線程。

這篇文章主要來介紹 Web Worker,包括什么時候使用,該怎么使用,它有什么奇怪的特性,會介紹在 Webpack 中如何使用它,還有可能遇到的一些坑。

一、Web Workers

Web Worker 可能是在 JavaScript 中唯一可以真正實現(xiàn)多線程的方法了。我們需要按照下面的方式創(chuàng)建 worker :

const worker = newWorker("worker.js");

上面就定義了一個 Worker 實例,然后你可以通過 postMessage 與 worker 通信,就像和 iFrame 通信一樣,只不過不存在跨域的問題,不需要驗證跨域。

worker.postMessage(num);

在 worker 代碼中,你需要監(jiān)聽這些事件:

onmessage = (e) => {
 // e.data will contain the value passed
};

這種方式是雙向的,所以你也可以從 worker 中 postMessage 給我們的主程序。

在 worker 代碼中:

postMessage(result);

在主程序中:

worker.onmessage = (e) => {}

這就是 worker 最基本的用法。

異常處理

在你的 worker 代碼中,有很多種方式來處理異常,比如你可以 catch 之后通過 postMessage 傳遞,這樣可能需要多些一些代碼,但是確實最有效也最安全的。

另一種方式是用 onerror 事件,這種方式可以捕捉所有未處理的異常,并且交給調(diào)用方來決定如何處理。調(diào)用方式很簡單:

worker.onerror = (e) => {};

為了調(diào)試方便,異常對象中還有一些額外的字段比如:filename,linenocolno.

回收

將不需要的 worker 回收是非常重要的,worker 會生成真正的操作系統(tǒng)線程,如果你發(fā)現(xiàn)與很多 worker 線程同時運行,你可以通過很簡單的殺掉瀏覽器進程。

你有兩種方式殺掉 worker 進程:在 worker 里和在 worker 外。我認為最好的處理 worker 生命周期的地方是在主頁面里,但這也要取決于你代碼的具體情況。

殺掉一個 worker 實例,在外部可以直接調(diào)用 terminate()方法,這種方法可以立即殺掉它,釋放所有它正在使用的資源,如果它正在運行,也會立即終止。

如果你想要讓 worker 自己去管理它的生命周期,可以直接在 worker 代碼中調(diào)用stop()方法。

不管使用哪種方法,worker 都會停止,銷毀所有資源。

如果你想使用一種“一次性”的 worker,比如需要做一些復(fù)雜運算之后就不再使用了,也要確保在 onerror 事件中去銷毀它們,這樣有利于規(guī)避一些難以發(fā)現(xiàn)的問題。

worker.onerror = (e) => {
 worker.terminate();
 reject(e);
};
worker.onmessage = (e) => {
 worker.terminate();
 resolve(e.data);
}

二、行內(nèi) Workers

有些時候?qū)?worker 代碼寫到一個外部文件可能會使原本簡單的問題變得復(fù)雜,幸運的是,workers 也可以用一個 Blob 來初始化。

寫一個行內(nèi) worker ,參考如下代碼段:

<!-- http://stackoverflow.com/a/6454685/2032154 -->
<script id="worker" type="javascript/worker">
 // Put your worker code here
</script>
const code = URL.createObjectURL(new Blob([
 document.getElementById("worker").textContent
]));
const worker = new Worker(code);

這樣你就創(chuàng)建了一個全局的 ObjectURL,但別忘了當(dāng)不需要的時候要銷毀它:

worker.terminate();
URL.revokeObjectURL(code);

三、Workers 嵌套

理論上,你可以嵌套使用 worker,就像在主線程中定義一個 worker 一樣。這里有一個簡單的 例子。但是不幸的是在 Chrome 中一直存在一個 bug ,讓我們不能愉快的玩耍,或許以后這個 bug 會修復(fù),但是目前來說還是沒有太多進展,所以你最好不要使用。

數(shù)據(jù)傳遞

在 worker 數(shù)據(jù)傳遞的過程中有些需要注意的邊緣情況。你可以傳遞數(shù)值,字符串,數(shù)組,也可以傳遞序列化/反序列化的對象。然而,你卻不應(yīng)該依賴序列化來保持數(shù)據(jù)結(jié)構(gòu),實際上,postMessage 用到了一種 數(shù)據(jù)克隆算法,它會生成一些額外的屬性比如 RegExps 和 Blobs 以及一些循環(huán)引用。

這就是說,你需要將你要傳遞的數(shù)據(jù)最小化。你不可以傳遞 functions ,即使是支持的類型也會有一些限制,這些也很容易產(chǎn)生一些難以發(fā)現(xiàn)的 bug。如果你將你的 API 定義為只傳遞字符串,數(shù)值,數(shù)組和對象的話,那你可能會避過這些問題。

循環(huán)引用

如果你有一個很復(fù)雜的對象,那么里面很可能存在循環(huán)引用,這時如果你將它序列化成 JSON,你將會得到一個 TypeError: Converting circular structure to JSON.

let a = {};
let b = {a};
a.b = b;
JSON.stringify({a,b}); // Error

然而你可以在 postMessage 中放心的使用,從而你就可以在 worker 中使用。

Transferable objects

為了防止同時修改同一變量的場景,你傳遞給 postMessage 的所有變量都會復(fù)制一份,這樣確保了你多個線程不會修改同一個變量。但如果你想要傳一個非常大的數(shù)據(jù)的話,你就會發(fā)現(xiàn)復(fù)制操作是很慢的。比如,如果你在做一些圖片相關(guān)的運算,你可能會傳遞整個圖片信息,就可能會遇到復(fù)制性能的瓶頸。

好在有 transferable object ,用 transfer 來代替 copy,比如ArrayBuffer 是transferable對象,而我們可以把任何類型的對象放在 ArrayBuffer 中。

如果你 transfer 一個對象,之前擁有它的線程被鎖定權(quán)限,它確保了數(shù)據(jù)沒有復(fù)制之前,不會被同時修改。

這時 postMessage 的代碼段就有點尷尬了:

const ab = new ArrayBuffer(100);
console.log(ab.byteLength); // 100
worker.postMessage(ab, [ab]);
console.log(ab.byteLength); // 0

確保在 postMessage 中傳遞第二個參數(shù),否則數(shù)據(jù)將會被復(fù)制。

const ab = new ArrayBuffer(100);
console.log(ab.byteLength); // 100
worker.postMessage(ab);
console.log(ab.byteLength); // 100

四、Webpack

在 Webpack 中使用 Web worker 時,你需要用 worker-loader。將它添加到 package.json 中的 devDependencies,然后運行 npm install,就可以了。

用到 worker 時,只需要 require 它。

const workerCode = require("worker!./worker.js");
...
const worker = new workerCode();

這樣就初始化了 worker,然后就像上面講的一樣使用 worker。

如果需要使用行內(nèi) worker,你需要傳遞 inline 參數(shù)給 loader。

const workerCode = require("worker?inline!./worker.js");
...
const worker = new workerCode();

在 worker 中你也可以 import 模塊。

import fibonacci from "./fibonacci.js";
...
const result = fibonacci(num);

缺點

在 Webpack 中使用 worker 很簡單,但是在使用時也有一些坑值得你注意。

首先,無法將代碼共用部分提取出來。如果你的 worker 中依賴一段共用代碼,你只能把代碼添加到 worker 中,不管其他地方是否也用到同樣的代碼。而且如果你多個 worker 要用同樣的庫,你也需要在每個 worker 中引入它們。

你可能會想如果你不用 worker-loader,然后用CommonsChunkPlugin指定一個新的入口,可能會解決這個問題。但是不幸的是 worker 不像是瀏覽器 window ,一些 feature 不可用,所以一些代碼必須要引入。

同時,用行內(nèi) worker 也不會解決問題,共用的代碼依然會出現(xiàn)在多個地方。

第二點缺點是,行內(nèi) worker 可能會導(dǎo)致 ObjectURLs內(nèi)存泄露.它們被創(chuàng)建出來以后就不會被釋放。這雖然不是一個大問題,但是如果你有很多“一次性” worker 的話,就會影響性能。

綜上所述,我個人建議是使用標準的 worker,注意在 worker 中引入了什么。還要注意使用緩存。

五、IFrames Web worker

IFrames Web worker 和 IFrame 很像,而且印象中 IFrame 也可以實現(xiàn)多線程。但是 IFrame 存在一些不是線程安全 API,比如 DOM 相關(guān),瀏覽器不能為他們生成新的線程,參考這里.

在 IFrame 跨域中,很多 API 它都沒有權(quán)限,也只能通過 postMessage,就像 Web Worker 一樣。理論上,瀏覽器可以在不同的線程中運行 IFrame,也就可以用 IFrame 實現(xiàn)多線程。

但是實際并非如此,它還是單線程的,瀏覽器不會給它們額外的線程。

總結(jié)

Web Worker 解決了 JavaScript 一直以來的大難題,盡管它的語法有些奇怪而且有很多限制,但是它卻可以真真正正的解決問題。從另外一方面來講,它也還是個嬰兒,某些方面還不是很成熟,不能讓我們完全依賴,所以這個技術(shù)普及還有一段距離,目前適用場景也比較局限。所以說,如果你需要做多線程,不要再等待其他的什么技術(shù),學(xué)習(xí) web worker 的邊緣問題,避開它的坑,你就可以很好的提高用戶體驗。以上就是這篇文章的全部內(nèi)容,希望對大家能有所幫助。

相關(guān)文章

  • JavaScript實現(xiàn)瀏覽器內(nèi)多個標簽頁通信方式詳解

    JavaScript實現(xiàn)瀏覽器內(nèi)多個標簽頁通信方式詳解

    在瀏覽器中,多個標簽頁(頁簽)之間的通信有一些方法可以實現(xiàn),這篇文章為大家整理了6個常用的方法,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-03-03
  • JavaScript手機振動API

    JavaScript手機振動API

    現(xiàn)代瀏覽器里提供的新的API越來越傾向于移動手機應(yīng)用,這篇文章主要為大家詳細介紹了JavaScript手機振動API的相關(guān)資料,需要的朋友可以參考下
    2016-06-06
  • js實現(xiàn)上下滑動輪播

    js實現(xiàn)上下滑動輪播

    這篇文章主要為大家詳細介紹了js實現(xiàn)上下滑動輪播,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • js使用split函數(shù)按照多個字符對字符串進行分割的方法

    js使用split函數(shù)按照多個字符對字符串進行分割的方法

    這篇文章主要介紹了js使用split函數(shù)按照多個字符對字符串進行分割的方法,實例分析了split函數(shù)的使用技巧,非常具有實用價值,需要的朋友可以參考下
    2015-03-03
  • JavaScript內(nèi)存管理與閉包實例詳解

    JavaScript內(nèi)存管理與閉包實例詳解

    不管什么樣的編程語言,在代碼的執(zhí)行過程中都是需要給它分配內(nèi)存的,下面這篇文章主要給大家介紹了關(guān)于JavaScript內(nèi)存管理與閉包的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-06-06
  • JavaScript列表框listbox全選和反選的實現(xiàn)方法

    JavaScript列表框listbox全選和反選的實現(xiàn)方法

    這篇文章主要介紹了JavaScript列表框listbox全選和反選的實現(xiàn)方法,涉及javascript操作列表框listbox的技巧,非常具有實用價值,需要的朋友可以參考下
    2015-03-03
  • js點擊按鈕實現(xiàn)多張圖片循環(huán)切換

    js點擊按鈕實現(xiàn)多張圖片循環(huán)切換

    這篇文章主要為大家詳細介紹了js點擊按鈕實現(xiàn)多張圖片循環(huán)切換,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • ionic2懶加載配置詳解

    ionic2懶加載配置詳解

    本篇文章主要介紹了ionic2懶加載配置詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • js判斷一個對象是數(shù)組(函數(shù))的方法實例

    js判斷一個對象是數(shù)組(函數(shù))的方法實例

    這篇文章主要給大家介紹了關(guān)于利用js如何判斷一個對象是數(shù)組(函數(shù))的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用JS具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 微信小程序?qū)崿F(xiàn)傳參數(shù)的幾種方法示例

    微信小程序?qū)崿F(xiàn)傳參數(shù)的幾種方法示例

    這篇文章主要給大家介紹了關(guān)于微信小程序?qū)崿F(xiàn)傳參數(shù)的幾種方法,分別是navigator跳轉(zhuǎn)時、全局變量、列表index下標取值以及form表單傳值的相關(guān)內(nèi)容,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01

最新評論

呈贡县| 张家界市| 石泉县| 乌恰县| 辽中县| 封开县| 东方市| 密云县| 阿城市| 遵义县| 镇江市| 南木林县| 弥勒县| 临江市| 岱山县| 阿巴嘎旗| 南昌市| 贵定县| 德江县| 孟州市| 阿巴嘎旗| 兴宁市| 柳林县| 喀喇| 根河市| 聂荣县| 上林县| 芮城县| 祁门县| 鄂州市| 晴隆县| 新闻| 永吉县| 苍梧县| 盖州市| 海伦市| 柞水县| 都江堰市| 丽江市| 县级市| 磐安县|