var start = new Date() setTimeout(function () { var end = new Date console.log('Time elapsed:', end - start, 'ms') }, 500) while (new Date() - start < 1000) { }
有其餘語言能完成預期的功能嗎?Java, 在Java.util.Timer中,對於定時任務的解決方案是經過多線程手段實現的,任務對象存儲在任務隊列,由專門的調度線程,在新的子線程中完成任務的執行html
JavaScript的主要用途是與用戶互動,以及操做DOM。這決定了它只能是單線程,不然會帶來很複雜的同步問題。segmentfault
爲了利用多核CPU的計算能力,HTML5提出Web Worker標準,容許JavaScript腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做DOM。因此,這個新標準並無改變JavaScript單線程的本質。promise
JS執行時會造成調用棧,調用一個函數時,返回地址、參數、本地變量都會被推入棧中,若是當前正在運行的函數中調用另一個函數,則該函數相關內容也會被推入棧頂.該函數執行完畢,則會被彈出調用棧.變量也隨之彈出,因爲複雜類型值存放於堆中,所以彈出的只是指針,他們的值依然在堆中,由GC決定回收.瀏覽器
JavaScript 主線程擁有一個執行棧以及一個任務隊列多線程
遇到異步操做(例如:setTimeout, AJAX)時,異步操做會由瀏覽器(OS)執行,瀏覽器會在這些任務完成後,將事先定義的回調函數推入主線程的任務隊列(task queue)中,當主線程的執行棧清空以後會讀取task queue中的回調函數,當task queue被讀取完畢以後,主線程接着執行,從而進入一個無限的循環,這就是事件循環.異步
主線程執行棧 & 任務隊列 循環執行,構成事件循環函數
setTimeout()只是將事件插入了"任務隊列",必須等到當前代碼(執行棧)執行完,主線程纔會去執行它指定的回調函數。要是當前代碼耗時很長,有可能要等好久,因此並無辦法保證,回調函數必定會在setTimeout()指定的時間執行。oop
(function test() { setTimeout(function() {console.log(4)}, 0); new Promise(function executor(resolve) { console.log(1); for( var i=0 ; i<10000 ; i++ ) { i == 9999 && resolve(); } console.log(2); }).then(function() { console.log(5); }); console.log(3); })()
macrotask 和 microtask 是異步任務的兩種分類。在掛起任務時,JS 引擎會將全部任務按照類別分到這兩個隊列中,首先在 macrotask 的隊列(這個隊列也被叫作 task queue)中取出第一個任務,執行完畢後取出 microtask 隊列中的全部任務順序執行;以後再取 macrotask 任務,周而復始,直至兩個隊列的任務都取完。學習
所有代碼(script) macrotask -> microtask queue (含有promise.then) -> macrotask(setTimeout) -> 下一個microtaskspa
process.nextTick(function A() { console.log(1); process.nextTick(function B(){console.log(2);}); }); setTimeout(function timeout() { console.log('TIMEOUT FIRED'); }, 0)
new Promise(function(resolve) { console.log('glob1_promise'); resolve(); }).then(function() { console.log('glob1_then') }) process.nextTick(function() { console.log('glob1_nextTick'); })
經過學習函數調用棧,任務隊列,MacroTask, MicroTask
等概念,對js中的事件循環機制有更深的理解,在之後面對setTimeout, setInterval
等異步操做時,更清晰的理解其運行機制,避免寫出不可控的代碼。
https://zhuanlan.zhihu.com/p/...