flyc
La vie est drôle
與
你才單線程,你全家都單線程
FlyC
Bonus
break-point vs console panel
setTimout vs setInterval 的選擇
Web Worker
// javascript
// setTimeout
var timeoutSec = 1,
timeoutCounter = 1;
setTimeout(function eachTime() {
console.log('%c setTimeout: ' + timeoutCounter, 'color: blue');
timeoutCounter++;
setTimeout(eachTime, timeoutSec * 1000);
}, timeoutSec * 1000);
////////////////////////////////////////////
// setInterval
var intervalSec = 1,
intervalCounter = 1;
setInterval(function eachTime() {
console.log('%c setInterval: ' + intervalCounter, 'color: red');
intervalCounter++;
}, intervalSec * 1000);
在執行同步的funtion 時
兩者看不太出特別的差異
所以
// setTimeout
var timeoutSec = 1,
timeoutCounter = 1;
setTimeout(function eachTime() {
var message = '%c setTimeout: ' + timeoutCounter;
console.log(message, 'color: blue');
timeoutCounter++;
// 模擬api 呼叫
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, 3 * 1000);
}).then(function() {
console.log('api 回來囉!');
// 等api 回來後再繼續執行
setTimeout(eachTime, timeoutSec * 1000);
});
}, timeoutSec * 1000);
// setInterval
var intervalSec = 1,
intervalCounter = 1;
setInterval(function eachTime() {
var message = '%c setInterval: ' + intervalCounter;
console.log(message, 'color: red');
intervalCounter++;
// 模擬api 呼叫
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, 3 * 1000);
}).then(function() {
console.log('api 回來囉!');
// !! 糟糕,辦不到
});
}, intervalSec * 1000);
看來setTimout 和setInterval 出現了點差距
setInterval 做不到在api 回來之後才繼續執行
那麼,
如果都執行同步的超複雜function 呢?
// javascript
// setTimeout
var timeoutSec = 1,
timeoutCounter = 1;
setTimeout(function eachTime() {
var message = '%c setTimeout: ' + timeoutCounter;
console.log(message, 'color: blue');
timeoutCounter++;
setTimeout(eachTime, timeoutSec * 1000);
// suuuuper long
// 10w
for (var i = 0; i < 10000000; i++);
}, timeoutSec * 1000);
// setInterval
var intervalSec = 1,
intervalCounter = 1;
setInterval(function eachTime() {
var message = '%c setInterval: ' + intervalCounter;
console.log(message, 'color: red');
intervalCounter++;
// suuuuper long
// 10w
for (var i = 0; i < 10000000; i++);
}, intervalSec * 1000);
兩者還是沒有特別差異。
稍微做個總結:
1. setInterval 在開始執行之前會先「等待」
2. setTimeout 可以透過程式碼決定什麼時候執行下一次
那麼,來說個特例:
// javascript
// setTimeout
var timeoutSec = 1,
timeoutCounter = 1;
setTimeout(function eachTime() {
var message = '%c setTimeout: ' + timeoutCounter;
console.log(message, 'color: blue');
timeoutCounter++;
setTimeout(eachTime, timeoutSec * 1000);
// suuuuper long
// 1000w
for (var i = 0; i < 10000000; i++);
}, timeoutSec * 1000);
// setInterval
var intervalSec = 1,
intervalCounter = 1;
setInterval(function eachTime() {
var message = '%c setInterval: ' + intervalCounter;
console.log(message, 'color: red');
intervalCounter++;
// suuuuper long
// 1000w
for (var i = 0; i < 10000000; i++);
}, intervalSec * 1000);
請注意!
你的瀏覽器可能會崩潰
setTimeout 會等待上一次執行結束才執行
所以被卡住比較容易理解
但
setInterval 為什麼也會被卡住 ?
可以想像成,setInterval 的確把要執行的function 放到排隊隊伍裡了
但
由於每一個排隊的人的辦理時間都很長
所以
就算排隊隊伍人數「增加」的速度再快
都和辦理時間無關
只是一直在增加排隊隊伍而已
該怎麼辦呢 ?
增加辦理的窗口就好啦
就是Web Worker 啦
Web Worker ? 他能做什麼?
以非常簡單的方式說:
它可以把事情放到背景執行
而且是把很多事情放到背景執行
接著會以dedicated worker 做介紹
javascript
好了,還你
API
Web Worker
我繼續做我的事情
這個好麻煩喔給你做
一個很難的工作
勤勞地忙碌中
很難的工作的結果
用那個結果來做事
...
好啦
總算完成囉
(開始思考自己是不是
工具人
不過我說
身為一個javascript
怎麼可能只丟一個工作給工具人呢
所以,實際上會比較像這樣:
難
難
難
難
難
難
難
難
難
難
難
難
難
難
難
難
javascript
Web Worker
好啦
好啦
好啦
好啦
好啦
好啦
好啦
好啦
好啦
好啦
好啦
好啦
好啦
好啦
好啦
好啦
給你
給你
給你
給你
給你
給你
給你
給你
給你
不過我再說
身為一個javascript
怎麼可能只有一個工具人呢
所以,實際上更像是這樣:
javascript
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
Web Worker
好難喔
這裡要特別注意的是
javascript 當然是可以自己下樓買早餐的
不過,這樣實在太累了
如果有人能把早餐買好送到樓下...
那不就更好了
還有要注意的是
雖然工具人Web Worker 很好用
但
如果只是非常簡單的工作
打電話給工具人呼叫Web Worker 的成本可能會更高
這時候
還是自己買早餐執行會比較快
我們來看看程式碼吧!
# Mac terminal
mkdir webWorker
cd webWorker
touch index.html
touch main.js
touch web-worker.js
# windows cmd
mkdir webWorker
cd webWorker
type nul > idnex.html
type nul > main.js
type nul > web-worker.js
創檔案
在各個檔案貼上
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
</body>
<script src="main.js"></script>
</html>
// main.js
var webWorker = new Worker('./web-worker.js');
webWorker.postMessage('hello, ');
webWorker.onmessage = function(e) {
var receivedMessage = e.data;
console.log(receivedMessage);
}
// web-worker.js
var worker = this;
worker.onmessage = function(e) {
var receivedMessage = e.data,
sendMessage = receivedMessage + 'world!';
worker.postMessage(sendMessage);
}
等等,還沒
Web Worker 會用到一個很特殊的方式:
用javascript 呼叫javascript
所以我們得先創一個web-server 來做這件事情
推薦使用 http-server
簡單快速
#bash
#在剛剛創建的web-worker 資料夾內:
npm install --global http-server
#等待安裝完後
http-server
#用瀏覽器造訪出現的localhost 網址、開啟開發者視窗
如果看到這個,就成功囉
那麼,我們到底做了什麼呢?
先從main.js 開始看吧
// main.js
var webWorker = new Worker('./web-worker.js');
webWorker.postMessage('hello, ');
webWorker.onmessage = function(e) {
var receivedMessage = e.data;
console.log(receivedMessage);
}
從現在起
你就是我的工具人了
我打電話給你
說了 "hello, "
我等你的來電
那麼,工具人WebWorker那邊的狀況如何呢?
// web-worker.js
var worker = this;
worker.onmessage = function(e) {
var receivedMessage = e.data,
sendMessage = receivedMessage + 'world!';
worker.postMessage(sendMessage);
}
收到電話很開心
努力工作
工作完成!
趕緊打電話回去
來張兩邊的完成圖吧
// main.js
var webWorker = new Worker('./web-worker.js');
webWorker.postMessage('hello, ');
webWorker.onmessage = function(e) {
var receivedMessage = e.data;
console.log(receivedMessage);
}
// web-worker.js
var worker = this;
worker.onmessage = function(e) {
var receivedMessage = e.data,
sendMessage = receivedMessage + 'world!';
worker.postMessage(sendMessage);
}
從現在你你就是我的工具人了
打電話給你,快接
電話響了,趕快接
努力工作一波
工作完成! 打電話回去
工作做完了呢,很好
有了WebWorker 的幫忙
javascript 就有更多的時間可以做自己的事囉
你問時間在哪裡?
// main.js
var webWorker = new Worker('./web-worker.js');
webWorker.postMessage('hello, ');
/***
在這裏
***/
webWorker.onmessage = function(e) {
var receivedMessage = e.data;
console.log(receivedMessage);
}
在這裏
言歸正傳,我們把setTimout 和setInterval 放進來吧
// main.js
var webWorker = new Worker('./web-worker.js'),
style = 'color: #ff0063;'
var sec = 1,
jobNumber = 1;
setTimeout(function eachTime() {
setTimeout(eachTime, sec * 1000);
webWorker.postMessage({
message: '開始工作! 工作名稱: ' + jobNumber
});
jobNumber++;
}, sec * 1000);
webWorker.onmessage = function(e) {
var receivedMessage = e.data;
console.log('%cjavascript: 收到webWorker 的訊息囉!', style);
console.log('%cjavascript: 收到的訊息是: ' + receivedMessage, style);
}
// web-worker.js
var worker = this,
style = 'color: blue;'
worker.onmessage = function(e) {
var message = e.data.message;
console.log('');
console.log('%c---------------------------', style);
console.log('%cwebWorker 開始工作囉!', style);
console.log('%cwebWorker 收到的訊息是: ' + message, style);
console.log('%c努力工作中', style);
// 努力工作
for (var i = 0; i < 1000; i++);
console.log('%c工作結束囉!', style);
console.log('%c傳訊息給javascript: 我把' + message + ' 做完囉!', style);
worker.postMessage('我把' + message + ' 做完囉!');
}
試著增加工作量吧!
// web-worker.js
var worker = this,
style = 'color: blue;'
worker.onmessage = function(e) {
var message = e.data.message;
console.log('');
console.log('%c---------------------------', style);
console.log('%cwebWorker 開始工作囉!', style);
console.log('%cwebWorker 收到的訊息是: ' + message, style);
console.log('%c努力工作中', style);
// 努力工作
// 超負荷,超重loading
for (var i = 0; i < 1000000000; i++) {
if (i % 100000000 === 0) {
console.log('%c我還活著! 我正在努力工作! 不要拋棄我QwQ!', style);
}
}
console.log('%c工作結束囉!', style);
console.log('%c傳訊息給javascript: 我把' + message + ' 做完囉!', style);
worker.postMessage('我把' + message + ' 做完囉!');
}
放心跑,不會crash
他看起來快死了,暫停一下
我們發現
兩種顏色的訊息、
出現了 交錯 的現象
這正是最常見的多工 multi-thread 會出現的現象
那麼,怎麼證明呢 ?
怎麼證明在javascript 很忙沒時間看訊息的時候
WebWorker 沒有偷懶有在好好工作呢 ?
Alert
不,這不是警告,這是window.alert 的意思
先讓我們回到必須自己買早餐的年代
Text
// main.js
// var webWorker = new Worker('./web-worker.js');
var style = 'color: #ff0063;';
var sec = 1,
jobNumber = 1;
setTimeout(function eachTime() {
setTimeout(eachTime, sec * 1000);
/*
webWorker.postMessage({
message: '開始工作! 工作名稱: ' + jobNumber
});
*/
for (var i = 0; i < 1000000000; i++) {
if (i % 100000000 === 0) {
console.log('%c我只能自己買早餐', style);
}
}
console.log('%c早餐好ㄘ', style);
jobNumber++;
}, sec * 1000);
/*
webWorker.onmessage = function(e) {
var receivedMessage = e.data;
console.log('%cjavascript: 收到webWorker 的訊息囉!', style);
console.log('%cjavascript: 收到的訊息是: ' + receivedMessage, style);
}
*/
我們的目標,是在買完10 份早餐之前
讓他停住!
你會,停不下來 (
反而是等到早餐買完才停下來呢
為什麼?
因為窗口很忙,沒時間理會 alert
那麼,有了WebWork 的情況又如何呢?
// main.js
var webWorker = new Worker('./web-worker.js');
var style = 'color: #ff0063;';
var sec = 1,
jobNumber = 1;
setTimeout(function eachTime() {
setTimeout(eachTime, sec * 1000);
console.log('%c打電話給WebWorker', style);
webWorker.postMessage({
message: '幫我買早餐嘛!' + jobNumber
});
jobNumber++;
}, sec * 1000);
webWorker.onmessage = function(e) {
var receivedMessage = e.data;
console.log('%cjavascript: 早餐好ㄘ!', style);
}
// web-worker.js
var worker = this,
style = 'color: blue;'
worker.onmessage = function(e) {
var message = e.data.message;
console.log('');
console.log('%c---------------------------', style);
console.log('%c電話響了! 收到的訊息是: ' + message, style);
console.log('%cwebWorker 開始買早餐囉!', style);
console.log('%c努力排隊中', style);
// 努力工作
// 超負荷,超重loading
for (var i = 0; i < 1000000000; i++) {
if (i % 100000000 === 0) {
console.log('%c我還活著! 我正在努力排隊! 再等我一下QwQ!', style);
}
}
console.log('%c總算買到囉!', style);
console.log('%c來,這是早餐 ', style);
worker.postMessage('我把早餐送來囉!');
}
出現了一堆 交叉
原因是
WebWorker 還在排隊的時候
javascript 就又打給他啦
每一秒,就會打一次的狂call
那麼
加上 alert 的操作會怎樣呢 ?
javascript 的紅色訊息停下來了
WebWorker 則是很盡責地繼續買...
也太淒涼了吧WebWorker!!
相信已經對WebWorker 有多好用盡責有充分瞭解了
不過之前說到的
身為一個javascript
怎麼能只有一個工具人呢?
基於用javascript 呼叫javascript
該不會要創一大堆檔案吧?
不要,javascript 才不做這種事
這時候
Blob 就派上用場了
Blob? 誰啊
就是自己的工具人自己做啦
// main.js
// 不需要了!我自己做!
// var webWorker = new Worker('./web-worker.js');
var javascriptStyle = 'color: #ff0063;';
// WebWorker 製造機
function createWebWorker(webWorkerApplication) {
webWorkerApplication = webWorkerApplication ? webWorkerApplication : Function.prototype;
return new Worker(URL.createObjectURL(new Blob(['(' + webWorkerApplication + ')()'])));
}
// 工具人的人格
function webWorkerApplication() {
var workerStyle = 'color: blue;';
var worker = this;
console.log('%c我被製造出來了!', workerStyle);
worker.onmessage = function(e) {
console.log('%c被製造出來的我接到電話了!', workerStyle);
console.log('%c電話內容是: ' + e.data, workerStyle);
console.log('%c被製造出來的我打電話回去!', workerStyle);
worker.postMessage('是你製造了我嗎?');
}
}
// 自己的webWorker 自己做!
var my_OWN_WebWorker = createWebWorker(webWorkerApplication);
console.log('%c發訊息給我自己做的webWorker!', javascriptStyle);
my_OWN_WebWorker.postMessage('我的子民們,聽命!!!');
my_OWN_WebWorker.onmessage = function(e) {
console.log('%c我的子民回電了!', javascriptStyle);
console.log('%c子民傳給我的內容是: ' + e.data, javascriptStyle);
}
// 參考資料: https://medium.com/@roman01la/run-web-worker-with-a-function-rather-than-external-file-303add905a0
以後要多少工具人WebWorker
就可以不用依賴靜態檔
想做多少自己做就好啦
還記得之前說過的
WebWorker 是要
用javascript 呼叫javascript
所以我們要用一個server 環境來支援WebWorker 嗎?
但在可以用Blob 達成自己製造WebWorker 的情況
應該就不需要server 的環境了對吧?
也就是可以把http-server 關掉了對吧?
你說的沒錯 !
值得一提的是
由javascript 自己產生的WebWorker 會以一個特別的形式出現在Network Pannel 的記錄裡
這就留到下次再討論吧
做個總結
說到工具人
那
你們有聽過
大家的工具人嗎
shared worker
相較於專一的Dedicated worker
是可以在多個頁面同時存在的Web Worker
也就是大家的工具人
不多作介紹,就先到這邊吧
Bonus
break-point vs console panel
除錯方法之
var message = 'hello, world!';
console.log(message);
message = 'hello, console!';
console.log(message);
var message = 'hello, world!';
debugger
// 手動輸入message
message = 'hello, debugger!';
debugger
// 手動輸入message
有什麼不一樣呢?
Bonus
break-point vs console panel
沒有
才怪,沒有我寫這個做什麼
Bonus
break-point vs console panel
var reference = {
message: 'hello, world!'
};
console.log(reference);
reference.message = 'hello, console!';
console.log(reference);
var reference = {
message: 'hello, world!'
};
debugger
// 手動輸入reference
reference.message = 'hello, debugger!';
debugger
// 手動輸入reference
那這樣呢?
Bonus
break-point vs console panel
不是啊何老師你騙我啊
你不是說不一樣嗎
Bonus
break-point vs console panel
杜先生
你
有把那個物件節點點開嗎?
Bonus
break-point vs console panel
恭迎慈孤觀音 渡世靈顯四方
Bonus
break-point vs console panel
... 但如果沒有先點開的話、就算是debugger 也還是會顯示call by reference 的結果喔
所以在除錯時得更加小心才是
By flyc