1.JavaScript是一門單線程語言,單線程意味着他只有一個棧,一次只能去作一件事情,因此要知道他是如何去處理異步問題還得搞清楚JavaScript的代碼執行運行機制。javascript
首先咱們先寫一段簡單同步線程的代碼java
function one(n){console.trace(n);}
function tow(n){one(n)}
function three(n){tow(n)}
three(3)promise
如圖所示callStack中是正在執行的代碼,咱們使用console.trace()打印此時調用站(callStack)的狀況,下圖的執行結果讓咱們瞭解到函數的調用順序是一種後進先出的規律。瀏覽器
2.那麼它是如何解決異步問題的呢?(task queues)異步
咱們先提一個概念 「任務隊列(task queues)「函數
由如下代碼咱們能夠知道settimeout會延遲一秒執行,他在執行前會被放入一個任務隊列當中(task queues),並不會直接去執行,等待 callStack 中的事件所有完成的時候 event loop纔會去將taskQueues中的事件調到callStack中執行oop
console.log('start'); setTimeout(()=>{ console.log('waiting') } , 1000) console.log('end');
輸出結果 因爲setTimeout沒有返回值因此他會先拋出undefined再等待一秒鐘後打印fetch
3.異步隊列也有執行優先級!(Microtasks)this
1.macrotaskspa
在等待回調的任務當中他們也分兩種任務,macrotask queue就是咱們常說的task queue。Macrotask包括了setTimeout, Dom 操做 click/onLoad/mouse事件綁定,fetch then response這類操做。實際上這些都是瀏覽器提供的API,因此在執行時是有它們單獨的線程去進行操做。舉個例子,setTimeout()設置了2s的延遲,是瀏覽器設置了timer來計時,是另外的線程在等待2秒,js主線程不受影響,2s後回調函數再進入task queue。
(function() { console.log('this is the start'); setTimeout(function cb() { console.log('this is a msg from call back'); }); console.log('this is just a message'); setTimeout(function cb1() { console.log('this is a msg from call back1'); }, 0); console.log('this is the end'); })();
event loop會將先有返回的任務放入callstack中 若是cb若和cb1無時間差則按照先進先出的執行順序
2.Microtask
ES6提供了Promise來進行異步操做。microtask。同上也有一個隊列(job queue)來處理microtask。job queue擁有更高的優先級。每一個task結束後,都會檢查microtask隊列是否有任務在等待執行,根據先進先出,依次執行。
console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); Promise.resolve().then(function() { console.log('promise1'); }).then(function() { console.log('promise2'); });
因爲macrotask的隊列(task queues)比 Microtask 的隊列(job queues)優先級低因此setTImeout的打印內容最後執行
3.總結 event loop
eventloop 當callstack爲空時去檢查隊列是否有須要執行的任務,執行順序跟根據先進先出的規則執行,執行的Microtask(Promise) 隊列比macrotask(setTimeout/DOM events/fetch)隊列優先級高,Microtask 中的任務會先執行,當Microtask中的隊列任務所有執行完畢以後纔會去執行macrotask隊列中的任務,固然最大的前提仍是callstack得先執行完成
while (queue.waitForMessage()) { queue.processNextMessage(); }