其他人已確診的問題很好,瀏覽器的單線程性質。我將提供一個可能的解決方案:發電機。
下面是這表明了問題codepen: http://codepen.io/anon/pen/zZwXem?editors=1111
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 60000;
function log(message) {
const output = document.getElementById('output');
output.value = output.value + '\n' + message;
}
function asyncTask() {
log('Simulated websocket message')
}
function doWork() {
const timer = setInterval(1000, asyncTask);
let total = 0;
for (let i = 1; i < 100000000; i++) {
const foo = Math.log(i) * Math.sin(i);
total += foo;
}
log('The total is: '+ total);
clearInterval(timer);
}
當的doWork()方法是單擊「做工作」按鈕調用,從來沒有的AsyncTask運行,而UI鎖定。糟糕的用戶體驗。
以下示例使用生成器來運行長時間運行的任務。
http://codepen.io/anon/pen/jBmoPZ?editors=1111
//Basically disable codepen infinite loop detection, which is faulty for generators
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 120000;
let workTimer;
function log(message) {
const output = document.getElementById('output');
output.value = output.value + '\n' + message;
}
function asyncTask() {
log('Simulated websocket message')
}
let workGenerator = null;
function runWork() {
if (workGenerator === null) {
workGenerator = doWork();
}
const work = workGenerator.next();
if (work.done) {
log('The total is: '+ work.value);
workerGenerator = null;
} else {
workTimer = setTimeout(runWork,0);
}
}
function* doWork() {
const timer = setInterval(asyncTask,1000);
let total = 0;
for (let i = 1; i < 100000000; i++) {
if (i % 100000 === 0) {
yield;
}
if (i % 1000000 == 0) {
log((i/100000000 * 100).toFixed(1) + '% complete');
}
const foo = Math.log(i) * Math.sin(i);
total += foo;
}
clearInterval(timer);
return total;
}
在這裏,我們在發電機不工作,並創建一個發電機轉輪從「做工作」在UI按鈕調用。這運行在最新版本的Chrome上,我不能說其他瀏覽器。通常情況下,您會使用類似babel的東西將生成器編譯爲生成構建的ES5語法。
生成器每計算10000行產生一次,並且每100000行發出一次狀態更新。生成器runner'runWork'創建生成器的一個實例並重復調用next()。生成器然後運行,直到它碰到下一個「yield」或return語句。在生成器產生之後,生成器運行器通過調用setTimeout以0毫秒的方式放棄UI線程並將其本身用作處理函數。這通常意味着它將在每個動畫幀(理想情況下)中調用一次。直到發電機返回完成標誌,此時發電機轉輪可以獲得返回的值並清理。
這裏的例子情況下,你需要重新創建codepen的HTML:
<input type='button' value='Do Work' onclick=doWork() />
<textarea id='output' style='width:200px;height:200px'></textarea>