JavaScript是一門單線程語言,即當有一個任務在執行的時候,其餘任務須要在後面等待。但在實際場景中,有些任務能夠放在比較靠後的位置,好比加載網絡資源時發送的異步請求。網絡
事件循環就是JavaScript異步執行機制的一種實現方式。異步
前面說到執行上下文存在建立階段和執行階段,這一章節主要針對執行階段來說。即在分析JavaScript代碼執行時,首先要作一次執行上下文建立階段的分析,然後才能利用這篇文章的知識點來分析JavaScript的執行機制。函數
咱們常說JavaScript是按照代碼順序一行行執行下來的,但這段代碼實際輸出的順序是2,1。這是爲何呢?post
setTimeout(function(){ console.log(1); },0) console.log(2);
JavaScript引擎會智能地將任務分紅同步任務和異步任務。spa
首先JavaScript引擎會將總體script代碼放入執行棧中,遇到同步任務則直接進入主線程執行,遇到異步任務時註冊回調函數,並將異步任務放進事件隊列中等待。線程
待主線程中的全部同步任務執行完畢後,纔會去事件隊列處取任務。根據隊列先進先出的特色,最早進入事件隊列的異步任務會被率先執行。3d
而在實際場景中,並非從事件隊列一一取出異步任務直接執行這麼簡單。從宏觀定義上,咱們將JavaScript的任務分爲同步任務和異步任務,但在事件循環中,更加精細的分法是將任務分爲宏任務和微任務。不一樣類型的任務會進入不一樣的事件隊列中。code
在JavaScript引擎執行代碼的過程當中,首先將總體代碼做爲一個宏任務執行,遇到通常的同步代碼當即執行,遇到定時器相關如setTimeout這種宏任務代碼則先將其放入到宏任務事件隊列中,遇到Promise這種則將其catch、then函數放入到微任務隊列中。blog
總體代碼做爲第一次宏任務執行完畢後,JavaScript引擎會先到微任務隊列中看看有沒有任務,若有任務則將其所有執行,若是沒有則進入到下一次宏任務中。也就是說JavaScript執行機制是按照宏任務-微任務-宏任務-微任務...這樣子循環執行的。隊列
根據隊列先進先出的特色,取出宏任務事件隊列中第一個宏任務率先執行,遇到同步代碼當即執行,遇到微任務則繼續進入微任務事件隊列。待這一次的宏任務執行完畢後,則繼續執行全部的微任務。如此循環下去,則就是事件循環。
先舉個簡單的例子
setTimeout(function(){ console.log(0); },3000); new Promise((resolve,reject)=>{ console.log(2); resolve(); }).then(()=>{ console.log('resolve'); } ); console.log(1)
第一遍事件循環過程:
第二遍事件循環過程:
上面給定時器的延遲時間是3秒鐘,有無多是時間片競爭的機制搶着輸出?試着把setTimeout的延遲時間改成0。
setTimeout(function(){ console.log(0); },0); new Promise((resolve,reject)=>{ console.log(2); resolve(); }).then(()=>{ console.log('resolve'); } ); console.log(1)
發現輸出的順序一致,實際上將延遲設置爲0,也不是當即執行。
依據上面的結論,將setTimeout的延遲時間設爲0的意思是待主線程空閒後即可以執行,還得等主線程按照宏任務-微任務-宏任務-微任務的事件循環順序執行完畢後。實際上即便主線程空閒了,延遲執行也不會達到0秒當即執行,最低也是4ms。
setTimeout
、setInterval
Promise
、nextTick
參考文章: https://juejin.im/post/59e85e...