有一個過程需要經過多個function計算後才能得出結果
而這一個過程會每隔一段時間就loop一次
ex.
第2個function需要第1個function計算過的資料
第3個function需要第2個function計算過的資料
以此類推...
這些function需要用同步的方式順序執行
最後會需要用計算出資料繪製在canvas上顯示出來
假設1個function可能要花掉50~100ms
因此會造成畫面上的狀態有卡頓的狀況發生
目前只想到將計算過程放到settimeout的callback中
這是我的範例
一個不斷上下移動的方塊,顯示計算對畫面造成阻塞的影響
<style>
.rectangle {
position: absolute;
left: 50%;
width: 150px;
height: 150px;
transform: translateX(-50%);
text-align: center;
background-color: #2196f3;
animation: move infinite 5s linear;
}
@keyframes move {
0% {top: 5%;background-color: #2196f3;}
50% {top: 80%;background-color: #9e9e9e;}
100% {top: 5%;background-color: #2196f3;}
}
</style>
<body>
<div class="rectangle">TEST</div>
<div id="state"></div>
</body>
function computeProcess(len) {
state.innerHTML = ""
for (let i = 0; i < len; i++) {
state.innerHTML = i;
}
}
function timeOut(callback, timer) {
setTimeout(() => {
callback();
}, timer);
}
function loop() {
// 會阻塞造成畫面卡頓
setTimeout(() => {
computeProcess(10000); // 第1個function
computeProcess(10000); // 第2個function
computeProcess(10000); // 第3個function
loop();
}, 300);
// 解決卡頓的作法???
timeOut(() => {
computeProcess(10000);
timeOut(() => {
computeProcess(10000);
timeOut(() => {
computeProcess(10000);
timeOut(() => {
loop();
}, 300);
}, 0);
}, 0);
}, 0);
}
loop();
但這樣變成Callback Hell,所以我現在是改成這樣做
讓function照順序執行然後也不要畫面有卡頓的狀況發生
不知道有沒有更好的方法?
function timeOut(callback, timer) {
return new Promise((resolve) => {
setTimeout(() => {
callback();
resolve();
}, timer);
});
}
function loop(){
(async () => {
await timeOut(() => {
computeProcess(10000);
}, 0);
await timeOut(() => {
computeProcess(10000);
}, 0);
await timeOut(() => {
computeProcess(10000);
}, 0);
await timeOut(() => {
loop();
}, 300);
})();
}
30 Seconds of Knowledge - chainAsync
如果我沒理解錯誤的話,您或許能參考上方的資源。
Web Workers 提供簡單的方法讓網頁在背景執行緒 (Thread) 中執行程式
運算量大的東西可以考慮丟給 Web Worker 跑
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="r">處理中</div>
</body>
</html>
<script>
function delay(n){
return new Promise(function(resolve){
setTimeout(resolve,n*1000);
});
}
let fn1 = async () => {
await delay(3) // delay請當成大量運算的時間
return 3
}
let fn2 = async (i) => {
await delay(2)
return i * 3
}
// 統合fn1、fn2,並等待fn2完成後發送事件告訴傳入的DOM該更新了。
let fn3 = async (elm) => {
const r1 = await fn1()
const r2 = await fn2(r1)
elm.dispatchEvent(new CustomEvent("onResult", {
detail: {r2: r2}
}));
}
const resultDisplay = document.querySelector("#r")
// 註冊事件
resultDisplay.addEventListener("onResult", function(e){
this.innerText = e.detail.r2
})
// 執行
fn3(resultDisplay)
</script>
https://jsbin.com/yujenaheti/edit?html,output
老慣例,ie一樣不能跑。
你要的大概是這樣吧。
delay的部份只是為了模擬運算時間。
真正大量運算的話用上面說的 Web Workers。
然後在javascript請盡量用event代替setTimeout...
你能確定你的運算不會delay到連你timeout的時間都超過?
稍微改一下code結構,為了解耦合。
不確定會不會delay到,不太清楚用event代替settimeout的意思是甚麼。
想問個,只要把耗時間的計算動作拆開,讓中間產生空檔讓GUI有空檔可以重新重繪,我的理解不知有沒有錯。
我能拆分的最小單位就是opencv.js的api,一個api的花掉的時間應該不多。
你注意看我的code,除了為了模擬大量運算的delay,應該沒有用到setTimeout的地方吧?
如果你都用setTimeout去等運算完成,你會遇到幾個問題:
因此最好的策略就是運算完就反應,而不是用setTimeout這種你無法掌握的方式。
如果你需要固定時序更新,可以先存到一個cache,定時取出更新。
我的code簡單的說就是首先先對#r註冊一個自訂事件(onResult),當這事件發生時,會更新畫面。
用fn3去等fn1、2的依序運算完成,觸發(dispatchEvent)一個自訂事件(onResult)。