web worker 初步學(xué)習(xí)并使用

Web Worker 概述

使用場(chǎng)景

js采用的是單線程模型阶冈,所有任務(wù)只能在一個(gè)線程上完成缤苫,但是計(jì)算機(jī)目前大部分都是多核cpu的诵叁,通過(guò)Web Worker為js創(chuàng)造多線程環(huán)境业栅。

主線程創(chuàng)建Worker線程冈欢,在主線程運(yùn)行的同時(shí)歉铝,Worder線程在后臺(tái)運(yùn)行,不會(huì)影響主線程涛癌,當(dāng)計(jì)算出結(jié)果后再將其返回給主線程犯戏。

可以將一些計(jì)算密集型或者高延遲的任務(wù)交給Worker線程,主線程主要來(lái)負(fù)責(zé)UI交互拳话,使頁(yè)面更流暢先匪。

注意

Worker線程新建成功后,會(huì)始終運(yùn)行弃衍,需要主動(dòng)關(guān)閉呀非。

主要是利于隨時(shí)響應(yīng)主進(jìn)程的通信。

  1. 同源限制

Worker線程運(yùn)行的文件必須和主線程的文件同源镜盯。

  1. DOM限制

Worker線程的全局對(duì)象與主線程不一樣岸裙,不能獲取DOM對(duì)象document速缆、window降允、parent

可以獲取 navigatorlocation艺糜。

  1. 通信聯(lián)系

不能直接通信剧董,必須通過(guò)消息完成幢尚。

  1. 腳本限制

Worker線程不能執(zhí)行alert()confirm(),但是可以執(zhí)行console翅楼,也可以使用XMLHttpRequest對(duì)象發(fā)出ajax尉剩。

  1. 文件限制

Worker線程不能讀取本地文件,必須來(lái)自網(wǎng)絡(luò)毅臊,本地的可以通過(guò)BlobURL.createObjectURL()實(shí)現(xiàn)理茎。

Web Worder使用

初步使用

主線程

  • 創(chuàng)建Worker線程和通信

/**
* 創(chuàng)建一個(gè)Worker線程
* worker.js必須來(lái)自網(wǎng)絡(luò),如果下載失敗不會(huì)有任何提示
*/
const worker = new Worker('worker.js');

/**
* 主線程向Worder線程發(fā)送數(shù)據(jù)
* 也可以傳二進(jìn)制數(shù)據(jù)
*/
worker.postMessage('test1');
worker.postMessage({
    method: 'send',
    arg: ['test2']
});

/**
* 主線程接收Worler線程發(fā)送數(shù)據(jù)
* 指定一個(gè)回調(diào)函數(shù)監(jiān)聽(tīng)
*/

worker.onmessage = (event) => {
    // event.data Worker線程發(fā)送到主線程的數(shù)據(jù)
    const data = event.data;
    // 告訴Worker線程可以關(guān)閉的信息管嬉,在Worker中處理關(guān)閉
    worker.postMessage('done');
    // 也可以主線程關(guān)閉
    worker.terminate();
}
  • 主線程可以監(jiān)聽(tīng)Worker是否出錯(cuò)皂林,如果出錯(cuò)會(huì)觸發(fā)主線程的error事件
worker.addEventListener('error', (e) => {
    console.log([
    'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
  ].join(''));
});
// 或
worker.onerror((e) => {
    console.log([
    'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
  ].join(''));
});
  • 關(guān)閉Worker
// 主線程
worker.terminate();
// Worker線程
self.close();

Worker線程

  • Worker線程內(nèi)部 selfthis代表子線程的自身

  • Worker線程內(nèi)部需要有一個(gè)監(jiān)聽(tīng)函數(shù),監(jiān)聽(tīng)message事件蚯撩。

三種寫法

// 1
self.addEventListener('message', (e) => {
    // 接收到的數(shù)據(jù)
    const data = e.data;
}, false);
// 2
this.addEventListener('message', (e) => {
    // 接收到的數(shù)據(jù)
    const data = e.data;
}, false);
// 3
addEventListener('message',  (e) => {
    // 接收到的數(shù)據(jù)
    const data = e.data;
}, false);

監(jiān)聽(tīng)除了監(jiān)聽(tīng)message事件式撼,也可以指定函數(shù)

  • 向主線程發(fā)送數(shù)據(jù)用self.postMessage()
self.onmessage = (e) => {
    const data = e.data;
    // 向主線程發(fā)送數(shù)據(jù)
    self.postMessage('Worker send');
}

  • 關(guān)閉Worker線程(關(guān)閉自身)
self.close();
  • Worker加載腳本

Worker內(nèi)部如果要加載其他的js,要用importScripts()

importScripts('a.js');
// 也可以同時(shí)加載多個(gè)js
importScripts('a.js', 'b.js');

數(shù)據(jù)通信

  • 主線程和Worker線程之間的通信求厕,除了二進(jìn)制File、Blob扰楼、ArrayBuffer等數(shù)據(jù)類型外呀癣,都是類似于JSON.parse(JSON.strify())這種傳值,并不會(huì)影響到本線程以外的數(shù)據(jù)改動(dòng)弦赖。

事實(shí)上项栏,瀏覽器內(nèi)部的運(yùn)行機(jī)制是,先將通信內(nèi)容串行化蹬竖,然后把串行化后的字符串發(fā)給 Worker沼沈,后者再將它還原。

  • 主線程和Worker線程之間交換二進(jìn)制數(shù)據(jù)币厕,上面拷貝的方式會(huì)造成性能問(wèn)題列另,因?yàn)橐话銛?shù)據(jù)量大的會(huì)用二進(jìn)制流方式去傳輸。為了避免這個(gè)問(wèn)題旦装,js允許主線程把二進(jìn)制數(shù)據(jù)直接轉(zhuǎn)移給worker線程页衙,但是在轉(zhuǎn)移沒(méi)有完成之前,主線程無(wú)法使用二進(jìn)制數(shù)據(jù)阴绢,這是為了防止出現(xiàn)多個(gè)線程同時(shí)修改數(shù)據(jù)的麻煩局面店乐。這種轉(zhuǎn)移數(shù)據(jù)的方法,叫做Transferable Objects呻袭。

Transferable Objects 格式:

worker.postMessage(arrayBuffer, [arrayBuffer]);

例如:

const ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);

例子

來(lái)源

  • Worker 線程完成輪詢

有時(shí)眨八,瀏覽器需要輪詢服務(wù)器狀態(tài),以便第一時(shí)間得知狀態(tài)改變左电。這個(gè)工作可以放在 Worker 里面廉侧。

function createWorker(f) {
  var blob = new Blob(['(' + f.toString() +')()']);
  var url = window.URL.createObjectURL(blob);
  var worker = new Worker(url);
  return worker;
}

var pollingWorker = createWorker(function (e) {
  var cache;

  function compare(new, old) { ... };

  setInterval(function () {
    fetch('/my-api-endpoint').then(function (res) {
      var data = res.json();

      if (!compare(data, cache)) {
        cache = data;
        self.postMessage(data);
      }
    })
  }, 1000)
});

pollingWorker.onmessage = function () {
  // render data
}

pollingWorker.postMessage('init');

常用API

主線程

  1. 瀏覽器原生提供Worker()構(gòu)造函數(shù)页响,用來(lái)供主線程生成 Worker 線程。
var myWorker = new Worker(jsUrl, options);

Worker()構(gòu)造函數(shù)伏穆,可以接受兩個(gè)參數(shù)拘泞。第一個(gè)參數(shù)是腳本的網(wǎng)址(必須遵守同源政策),該參數(shù)是必需的枕扫,且只能加載 JS 腳本陪腌,否則會(huì)報(bào)錯(cuò)。第二個(gè)參數(shù)是配置對(duì)象烟瞧,該對(duì)象可選诗鸭。它的一個(gè)作用就是指定 Worker 的名稱,用來(lái)區(qū)分多個(gè) Worker 線程参滴。

// 主線程
var myWorker = new Worker('worker.js', { name : 'myWorker' });

// Worker 線程
self.name // myWorker
  1. Worker()構(gòu)造函數(shù)返回一個(gè) Worker 線程對(duì)象强岸,用來(lái)供主線程操作 Worker。Worker 線程對(duì)象的屬性和方法如下:
    1. Worker.onerror:指定 error 事件的監(jiān)聽(tīng)函數(shù)砾赔。
    2. Worker.onmessage:指定 message 事件的監(jiān)聽(tīng)函數(shù)蝌箍,發(fā)送過(guò)來(lái)的數(shù)據(jù)在Event.data屬性中。
    3. Worker.onmessageerror:指定 messageerror 事件的監(jiān)聽(tīng)函數(shù)暴心。發(fā)送的數(shù)據(jù)無(wú)法序列化成字符串時(shí)妓盲,會(huì)觸發(fā)這個(gè)事件。
    4. Worker.postMessage():向 Worker 線程發(fā)送消息专普。
    5. Worker.terminate():立即終止 Worker 線程悯衬。

Worker線程

  1. self.name: Worker 的名字。該屬性只讀檀夹,由構(gòu)造函數(shù)指定筋粗。
  2. self.onmessage:指定message事件的監(jiān)聽(tīng)函數(shù)。
  3. self.onmessageerror:指定 messageerror 事件的監(jiān)聽(tīng)函數(shù)炸渡。發(fā)送的數(shù)據(jù)無(wú)法序列化成字符串時(shí)娜亿,會(huì)觸發(fā)這個(gè)事件。
  4. self.close():關(guān)閉 Worker 線程偶摔。
  5. self.postMessage():向產(chǎn)生這個(gè) Worker 線程發(fā)送消息暇唾。
  6. self.importScripts():加載 JS 腳本。

webpack應(yīng)用

worker-loader

安裝

yarn add -D worker-loader

webpack配置

  • webpack.config.js
{
  module: {
    rules: [
      {
        test: /\.worker\.js$/,
        use: { loader: 'worker-loader' }
      }
    ]
  }
}
  • vue.config.js(vue-cli3)
module.exports = {
    chainWebpack: (config) => {
        // 新增加一個(gè)loader
        config.module.rule('worker').test(/\.worker\.js$/).use('worker-loader').loader('worker-loader').options({ name: '[name].[hash].js' }).end()
    },
    // 如果開啟多進(jìn)程構(gòu)建辰斋,worker-loader 會(huì)報(bào) Cannot read property 'createChildCompiler' of undefined
    parallel: false
}

https://github.com/vuejs/vue-cli/issues/2785

項(xiàng)目中使用

  • file.worker.js

import marked from 'marked';

self.addEventListener('message', (e) => {
  const data = e.data
  const md = marked(data, { sanitize: true });
  self.postMessage(md)
})

  • App.js
import markdownWorder from './file.worker.js';

const worker = new markdownWorder()
const markdownText = `# demo

## demo1`

worker.postMessage(markdownText)

worker.onmessage = (event) => {
  const HTML = event.data
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末策州,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子宫仗,更是在濱河造成了極大的恐慌够挂,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件藕夫,死亡現(xiàn)場(chǎng)離奇詭異孽糖,居然都是意外死亡枯冈,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門办悟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)尘奏,“玉大人,你說(shuō)我怎么就攤上這事病蛉§偶樱” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵铺然,是天一觀的道長(zhǎng)俗孝。 經(jīng)常有香客問(wèn)我,道長(zhǎng)魄健,這世上最難降的妖魔是什么赋铝? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮沽瘦,結(jié)果婚禮上革骨,老公的妹妹穿的比我還像新娘。我一直安慰自己析恋,他們只是感情好苛蒲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绿满,像睡著了一般。 火紅的嫁衣襯著肌膚如雪窟扑。 梳的紋絲不亂的頭發(fā)上喇颁,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音嚎货,去河邊找鬼橘霎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛殖属,可吹牛的內(nèi)容都是我干的姐叁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼洗显,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼外潜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起挠唆,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤处窥,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后玄组,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滔驾,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谒麦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哆致。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绕德。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖摊阀,靈堂內(nèi)的尸體忽然破棺而出耻蛇,到底是詐尸還是另有隱情,我是刑警寧澤驹溃,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布城丧,位于F島的核電站,受9級(jí)特大地震影響豌鹤,放射性物質(zhì)發(fā)生泄漏亡哄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一布疙、第九天 我趴在偏房一處隱蔽的房頂上張望蚊惯。 院中可真熱鬧,春花似錦灵临、人聲如沸截型。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)宦焦。三九已至,卻和暖如春顿涣,著一層夾襖步出監(jiān)牢的瞬間波闹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工涛碑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留精堕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓蒲障,卻偏偏與公主長(zhǎng)得像歹篓,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子揉阎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一庄撮、概述 JavaScript 語(yǔ)言采用的是單線程模型,也就是說(shuō)毙籽,所有任務(wù)只能在一個(gè)線程上完成重窟,一次只能做一件事。...
    零星小雨_c84a閱讀 2,467評(píng)論 0 2
  • 作者:阮一峰www.ruanyifeng.com/blog/2018/07/web-worker.html 概述 ...
    grain先森閱讀 1,081評(píng)論 0 1
  • 介紹web worker HTML5提供得惧财,運(yùn)行在后臺(tái)的 JavaScript巡扇,獨(dú)立于其他腳本扭仁,不會(huì)影響頁(yè)面的性能...
    帶刀打天下閱讀 1,309評(píng)論 0 3
  • 參考阮一峰 Web Worker 使用教程web worker詳解youtube上worker視頻,完整代碼 如我...
    合肥黑閱讀 4,399評(píng)論 0 5
  • 上一篇:龍生九子––次子.睚眥 凡九種厅翔,皆一龍所生子也蚩吻一云嘲鳳乖坠。嘲鳳,瑞獸刀闷,什么獸呢熊泵?我描述一下哈:胡子一把,...
    饕餮思文閱讀 1,553評(píng)論 7 4