2017-03-02 39 views
4

有沒有更快的替代window.requestAnimationFrame()無限循環不阻止I/O?非常快的無限循環無阻塞I/O

我在循環中做的事情與動畫無關,所以我不在乎下一幀何時準備就緒,而且我已經讀取了window.requestAnimationFrame()被顯示器的刷新率限制,或者至少等到框架可以繪製。

我曾嘗試以下還有:

function myLoop() { 
    // stuff in loop 
    setTimeout(myLoop, 4); 
} 

(四是因爲這是在setTimeout和較小值的最小間隔將仍然默認爲4)不過,我需要比這更好的分辨率。

那裏有更好的表現嗎?

我基本上需要一個非阻塞版本的while(true)。將運行比setTimeout越早

+0

*「(這是因爲這是setTimeout中的最小間隔,較小的值仍將默認爲4)。」*這是[更復雜](https://www.w3.org/TR/html5/webappapis .html#dom-windowtimers-settimeout),並且至少改變了兩次。只需使用'0',讓實現擔心是否增加它。 –

+0

@ T.J。我正在使用Electron,所以我可以同時訪問節點模塊和window.blahblah :) – Joey

+0

如果您需要一秒鐘運行頻率超過250次,爲什麼不讓它一次運行多次迭代,然後允許對於I/O,重複?如果你想要一個小於4毫秒的真實分辨率,我認爲你在錯誤的操作系統上用錯誤的語言編程。 – ASDFGerte

回答

4

兩件事情:

  • process.nextTick回調(的NodeJS專用):

    process.nextTick()方法將回調到 「下剔隊列」。一旦事件循環的當前轉向運行完成,當前在下一個滴答隊列中的所有回調將被調用。

    這不是setTimeout(fn, 0)的簡單別名。它效率更高。它在任何其他I/O事件(包括定時器)在事件循環的後續觸發中觸發之前運行。

  • 無極結算通知

因此,這些可能是你的工具區一個工具,做一個或兩個那些setTimeout的組合,以達到你想要的平衡。

詳情:

正如你可能知道,一個給定的JavaScript線程任務隊列的基礎上運行(規範稱之爲作業隊列);正如你可能知道的那樣,瀏覽器中有一個主要的默認UI線程,NodeJS運行一個線程。

但事實上,也有現代的實現至少有兩個任務隊列:主要一個大家都認爲(其中setTimeout和事件處理程序把他們的任務),以及「microtask」隊列,其中某些異步操作過程中放置主任務(或「macrotask」)的處理。這些微任務一旦宏任務完成就立即被處理,之前主隊列中的下一個宏任務  —即使下一個宏任務在微任務之前排隊。

nextTick回調和承諾結算通知都是microtasks。因此,調度要麼調度異步回調,而要調度下一個主要任務之前發生的回調。

我們可以看到,與setInterval瀏覽器和一個承諾分辨率鏈:

let counter = 0; 
 

 
// setInterval schedules macrotasks 
 
let timer = setInterval(() => { 
 
    $("#ticker").text(++counter); 
 
}, 100); 
 

 
// Interrupt it 
 
$("#hog").on("click", function() { 
 
    let x = 300000; 
 

 
    // Queue a single microtask at the start 
 
    Promise.resolve().then(() => console.log(Date.now(), "Begin")); 
 

 
    // `next` schedules a 300k microtasks (promise settlement 
 
    // notifications), which jump ahead of the next task in the main 
 
    // task queue; then we add one at the end to say we're done 
 
    next().then(() => console.log(Date.now(), "End")); 
 

 
    function next() { 
 
    if (--x > 0) { 
 
     if (x === 150000) { 
 
     // In the middle; queue one in the middle 
 
     Promise.resolve().then(function() { 
 
      console.log(Date.now(), "Middle"); 
 
     }); 
 
     } 
 
     return Promise.resolve().then(next); 
 
    } else { 
 
     return 0; 
 
    } 
 
    } 
 
}); 
 

 
$("#stop").on("click", function() { 
 
    clearInterval(timer); 
 
});
<div id="ticker">&nbsp;</div> 
 
<div><input id="stop" type="button" value="Stop"></div> 
 
<div><input id="hog" type="button" value="Hog"></div> 
 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

當您運行並點擊生豬按鈕,注意如何,計數器顯示凍結,然後繼續前進。這是因爲30萬個微任務會在它之前進行。還要注意我們編寫的三條日誌消息的時間戳(它們不會出現在代碼控制檯中,直到macrotask顯示它們,但是時間戳會顯示我們何時記錄它們)。所以基本上,你可以安排一堆微任務,並且定期讓這些微任務運行並運行下一個宏任務。


注意:我用setInterval在片斷瀏覽器的例子,但setInterval,具體而言,可能不使用的NodeJS了類似的實驗,一個不錯的選擇,爲的NodeJS的setInterval是有點不同這是瀏覽器中的一個,並且具有一些令人驚訝的時序特徵。

+0

謝謝,雖然這並沒有最終成爲我用例的正確解決方案,但我給了你一個複選標記,因爲它正確地回答了問題 – Joey