2017-03-09 46 views
1

我通過websockets(paho-mqtt)在瀏覽器中接收數據,但問題是僅當另一個任務結束時纔會觸發接收回調(大循環),並且它被觸發所有的堆積數據,我不會丟失數據只是延遲。即使有循環運行,該回調是不是應該被解僱?這裏發生了什麼?否則,我怎樣才能做到這一點,在循環內繼續接收?只有在for循環結束後纔會調用回調

我想要說的是等效於以下內容:

如果我這樣做是鉻

setTimeout(() => { 
    console.log('hello!'); 
}, 10); 
for (var i = 0; i < 50000; i++) { 
    console.log('for array'); 
} 

我得到

50000 VM15292:5 for array 
VM15292:2 hello! 

我不應該得到這樣的事情這個?

1000 VM15292:5 for array 
VM15292:2 hello! 
49000 VM15292:5 for array 

回答

0

其他人已確診的問題很好,瀏覽器的單線程性質。我將提供一個可能的解決方案:發電機。

下面是這表明了問題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> 
0

Javascript引擎往往是單線程的。

所以,如果你是在一個長期運行的緊密循環不產生(例如做一些IO),則回調將永遠不會有機會運行,直到循環結束

3

當你運行JavaScript代碼瀏覽器(除非使用Web Workers或其他特殊技術),它將在單個線程上執行。這聽起來可能不太重要,但事實確實如此。

您的代碼由for循環(同步)和setTimeout(異步)調用組成。由於一次只能運行一段JavaScript,因此您的for-loop將永遠不會被setTimeout中斷。

事實上,如果你的for循環包含在需要超過10毫秒即可完成非常密集的操作,您setTimeout回調實際上可能是延遲過去那個標記,因爲瀏覽器總是等待當前執行的代碼完成然後繼續運行事件循環。

setTimeout(() => { 
 
    console.log('hello!'); 
 
}, 10); 
 
for (var i = 0; i < /* 50000 */ 5; i++) { 
 
    console.log('for array'); 
 
}