前言
來啦老鐵喊儡!
今天咱們暫不學(xué)習(xí)Spring Boot拨与,如果您對Spring Boot感興趣,麻煩移步艾猜、關(guān)注專題:
近期在搭建公司W(wǎng)eb視頻編輯器SDK的接口自動化框架過程中捻悯,遇到幾個新名詞,是我之前所不了解的淤毛,他們就是:
-
Web Worker今缚;
-
Blob;
筆者自詡對Node.js還算玩的溜低淡,沒曾想在前端javascript還是半個文盲姓言,您說咱們是不是得補一補?
整體學(xué)習(xí)路徑
- Web Worker的由來蔗蹋;
- 什么是Web Worker?
- 動手實踐Web Worker何荚;
- 什么是Blob?
- 使用Blob動態(tài)創(chuàng)建Web Worker;
1. Web Worker的由來猪杭;
為了解釋W(xué)eb Worker的由來餐塘,我們先一起來學(xué)習(xí)一下js單線程、異步的原理皂吮。簡單寫了點js代碼并簡單作了一下圖戒傻,用于解釋js單線程、異步的原理:
一起來理解一下:
js是單線程的需纳、異步的?
眾所周知狂票,js卻是是單線程的候齿,也的確是異步的,很多人都深信不疑闺属,但是我告訴您慌盯,這句話是錯的,至少不嚴謹掂器!怎么說亚皂?原因是:單線程與異步本身是矛盾的,單線程一定是同步的国瓮,即代碼是一行一行執(zhí)行的灭必!
可是js的確是異步的,這又怎么解釋乃摹?
js本身的確是單線程禁漓、同步的,但瀏覽器不是呀孵睬,瀏覽器是多線程的播歼,瀏覽器為js提供了任務(wù)隊列線程。
js主線程一行一行地執(zhí)行代碼掰读,當遇到耗時的代碼秘狞,就把該任務(wù)丟到瀏覽器提供的任務(wù)隊列里頭叭莫,然后js主線程繼續(xù)往下執(zhí)行代碼匿辩;
當js主線程執(zhí)行完不阻塞的代碼后讥蔽,再到瀏覽器任務(wù)隊列里頭循環(huán)查找那些耗時的任務(wù)執(zhí)行情況帕胆,當耗時的任務(wù)執(zhí)行完畢后粒督,js就能拿到耗時任務(wù)的結(jié)果,也即js主線程才真正完成使命覆积!
因此龟糕,在上述“js單線程沪斟、異步原理圖”中辩蛋,實際的執(zhí)行應(yīng)該是像藍色的箭頭所示呻畸。當js主線程執(zhí)行demo()方法時,先打印出數(shù)字1悼院,接著打印數(shù)字3,最后才打印出數(shù)字2咒循,因為setTimeout是阻塞的(事實上据途,即使setTimeout等待的時間為0,數(shù)字2也是最后打印的)叙甸,如圖:
js主線程+瀏覽器的任務(wù)隊列線程颖医,才使js達到了異步的效果!nodejs也是類似的!
可是裆蒸,這跟Web Worker有啥關(guān)系呀熔萧?
仔細看下“js單線程僚祷、異步原理圖”佛致,瀏覽器隊列只有一個,而且既然是隊列辙谜,就需要排隊俺榆,很多業(yè)務(wù)場景下如計算密集型或高延遲任務(wù)中還是不夠用的,這時候就衍生出Web Worker装哆!
2. 什么是Web Worker?
來自網(wǎng)絡(luò)文章:Web Worker 的作用罐脊,就是為 JavaScript 創(chuàng)造多線程環(huán)境,允許主線程創(chuàng)建 Worker 線程蜕琴,將一些任務(wù)分配給后者運行萍桌。在主線程運行的同時,Worker 線程在后臺運行凌简,兩者互不干擾上炎。等到 Worker 線程完成計算任務(wù),再把結(jié)果返回給主線程号醉。這樣的好處是反症,一些計算密集型或高延遲的任務(wù)辛块,被 Worker 線程負擔(dān)了,主線程(通常負責(zé) UI 交互)就會很流暢铅碍,不會被阻塞或拖慢润绵。
引入Web Worker后就變成這樣了:
每個Web Worker單獨占用一個線程(即多線程),Web Worker線程與Web Worker線程間是互不干擾的胞谈!
想象一下尘盼,在未引入Web Worker的時候,假設(shè)任務(wù)7是計算密集型或高延遲的任務(wù)烦绳,而任務(wù)8是某UI交互卿捎,則UI交互就會被計算密集型或高延遲的任務(wù)所阻塞,頁面很可能就是卡住径密、無響應(yīng)的狀態(tài)午阵!
而引入Web Worker之后,計算密集型或高延遲的任務(wù)被安排到了新的Web Worker線程享扔,并不占用瀏覽器的任務(wù)隊列底桂,即像任務(wù)8就不會被阻塞,頁面就能很流暢惧眠!
需要注意的是:由于每個Web Worker均占用新的線程籽懦,而線程就會占用系統(tǒng)資源,因此氛魁,Web Worker一旦使用完畢暮顺,就應(yīng)該關(guān)閉,不應(yīng)該過度使用秀存!
Web Worker有一些限制(來源于網(wǎng)絡(luò)文章)捶码,具體是:
1. 同源限制;
分配給 Worker 線程運行的腳本文件应又,必須與主線程的腳本文件同源宙项。
2. DOM 限制;
Worker 線程所在的全局對象株扛,與主線程不一樣尤筐,無法讀取主線程所在網(wǎng)頁的 DOM 對象,也無法使用document洞就、window盆繁、parent這些對象。但是旬蟋,Worker 線程可以navigator對象和location對象油昂。
3. 通信聯(lián)系;
Worker 線程和主線程不在同一個上下文環(huán)境,它們不能直接通信冕碟,必須通過消息完成拦惋。
4. 腳本限制;
Worker 線程不能執(zhí)行alert()方法和confirm()方法安寺,但可以使用 XMLHttpRequest 對象發(fā)出 AJAX 請求厕妖。
5. 文件限制;
Worker 線程無法讀取本地文件挑庶,即不能打開本機的文件系統(tǒng)(file://)言秸,它所加載的腳本,必須來自網(wǎng)絡(luò)迎捺。
3. 動手實踐Web Worker举畸;
1. 編寫html頁面,如:index.html凳枝;
<!DOCTYPE html>
<html>
<head>
<title>Web Worker實踐</title>
<meta charset="utf-8">
</head>
<body>
<p>計數(shù): <output id="result"></output></p>
<button onclick="startWorker()">開始計數(shù)</button>
<button onclick="stopWorker()">停止計數(shù)</button>
<button onclick="alert(123)">Alert操作</button>
</body>
</html>
<script>
let worker;
function startWorker() {
worker = new Worker('./index.js');
worker.onmessage = function(event) {
document.getElementById('result').innerHTML = event.data;
}
}
function stopWorker() {
worker.terminate();
}
</script>
2. 編寫要worker的任務(wù)抄沮,如:index.js;
let i= 0;
function count() {
i += 1;
postMessage(i);
setTimeout(count(), 1000);
}
count();
3. 搭建一個簡單服務(wù)器岖瑰;
根據(jù)Web Worker的文件限制合是,我們需要搭建一個簡單服務(wù)器,來運行我們的例子锭环。服務(wù)器搭建可參考:
直接復(fù)制其代碼,織入新建的main.js文件即可:
var http = require('http');
var fs = require('fs');
var url = require('url');
// 創(chuàng)建服務(wù)器
http.createServer( function (request, response) {
// 解析請求泊藕,包括文件名
var pathname = url.parse(request.url).pathname;
// 輸出請求的文件名
console.log("Request for " + pathname + " received.");
// 從文件系統(tǒng)中讀取請求的文件內(nèi)容
fs.readFile(pathname.substr(1), function (err, data) {
if (err) {
console.log(err);
// HTTP 狀態(tài)碼: 404 : NOT FOUND
// Content Type: text/html
response.writeHead(404, {'Content-Type': 'text/html'});
}else{
// HTTP 狀態(tài)碼: 200 : OK
// Content Type: text/html
response.writeHead(200, {'Content-Type': 'text/html'});
// 響應(yīng)文件內(nèi)容
response.write(data.toString());
}
// 發(fā)送響應(yīng)數(shù)據(jù)
response.end();
});
}).listen(8080);
// 控制臺會輸出以下信息
console.log('Server running at http://127.0.0.1:8080/');
index.html辅辩、index.js、main.js在同一個文件目錄下即可娃圆!
4. Web Worker演示玫锋;
1). 啟動項目:
node main.js
2). 訪問html頁面;
3). 驗證Web Worker工作情況讼呢;
- 開始計數(shù):
- 點擊Alert操作按鈕并等待幾秒:
- 關(guān)閉Alert窗口:
- 停止計數(shù):
我們會發(fā)現(xiàn)撩鹿,當alert窗口彈出時,Worker在后臺還一直執(zhí)行悦屏,并不會阻礙瀏覽器的其他事件节沦!停止計數(shù)后,再次開始計數(shù)也是從1開始數(shù)础爬。
通過這個簡單的例子甫贯,我們就能感受到Web Worker的效果,當計數(shù)Worker為其他計算密集型或高延遲的任務(wù)時看蚜,效果應(yīng)該會更加明顯叫搁!
4. 什么是Blob?
對于什么是Blob,讀者可自行查閱資料,我們這里只針對Web Worker來介紹Blob渴逻。
上面的例子中疾党,我們將模擬的計算密集型或高延遲的任務(wù)寫在index.js文件中,
但是如果在實際生產(chǎn)中惨奕,我們只能運行一個事先寫好的js文件雪位,那這worker的局限性就太大了,太不靈活了墓贿,事實上茧泪,我們往往需要一個動態(tài)創(chuàng)建Web Worker的能力,通常我們利用Blob以及URL.createObjectURL()來提供動態(tài)創(chuàng)建Web Worker的能力聋袋!
5. 使用Blob動態(tài)創(chuàng)建Web Worker队伟;
要使用Blob動態(tài)創(chuàng)建Web Worker,簡單的說就是:
let worker = new Worker('./index.js');
要變成:
let blob = new Blob(XXX)
let worker = new Worker(URL.createObjectURL(blob));
XXX指的是要動態(tài)執(zhí)行的任務(wù)幽勒!
動手擼一個示例:
將index.html進行改造:
<!DOCTYPE html>
<html>
<head>
<title>Web Worker實踐</title>
<meta charset="utf-8">
</head>
<body>
<p>計數(shù): <output id="result"></output></p>
<button onclick="startWorker()">開始計數(shù)</button>
<button onclick="stopWorker()">停止計數(shù)</button>
<button onclick="alert(123)">Alert操作</button>
</body>
</html>
<script>
function createBlob() {
function count() {
i+=1;
postMessage(i);
setTimeout("count()", 1000);
}
let blob = new Blob(["let i= 0;" + count.toString() + ' count()'], {type: 'text/javascript'});
return URL.createObjectURL(blob);
}
let worker;
function startWorker() {
worker = new Worker(createBlob());
worker.onmessage = function(event) {
document.getElementById('result').innerHTML = event.data;
}
}
function stopWorker() {
worker.terminate();
}
</script>
簡單的說URL.createObjectURL();就是把代碼與url做個映射關(guān)聯(lián)嗜侮,url的返回就是Blob對象包裝的任務(wù)代碼!
運行后跟之前是一樣的效果喲啥容!
這樣锈颗,我們就擺脫了需要事先寫好js文件的束縛,能夠動態(tài)地創(chuàng)建Web Worker嘍咪惠!
綜上击吱,我們用一句話概括Web Worker和Blob:
- Web Worker:使得js有多線程效果!
- Blob:可以利用Blob動態(tài)創(chuàng)建Web Worker!
如果本文對您有幫助遥昧,麻煩點贊覆醇、關(guān)注!
謝謝炭臭!