事件循環以及vue.nextTick的應用


事件循環

三個概念

調用棧,先進后出

宏任務隊列(存放宏任務的,隊列先進先出)

微任務隊列

異步任務又分為宏任務和微任務

宏任務,macrotask,也叫tasks

setTimeout

setInterval

setImmediate (Node獨有)

requestAnimationFrame (瀏覽器獨有)

I/O

DOM/Web events (onclick, onkeydown, XMLHttpRequest etc)

UI rendering (瀏覽器獨有)

微任務,microtask,也叫jobs

process.nextTick (Node獨有)

Promise

Object.observe

MutationObserver

 

事件循環執行的過程

一輪事件循環只取一個宏任務,宏任務中的同步代碼執行完后,就依次從前往后執行微任務隊列中的微任務

 

代碼執行初期,調用棧為空,宏任務隊列中只有script這個宏任務,微任務隊列為空

將script這個宏任務放入調用棧中,執行同步代碼

遇到微任務,就將微任務加到微任務隊列中,遇到宏任務,就將宏任務加到宏任務隊列中,遇到函數調用的,就將該函數加到調用棧中,執行該函數中的同步代碼,遇到微任務或者是宏任務,就將它們加到對應的任務隊列中

等調用棧中的同步代碼執行完成后,查看微任務隊列中是否有微任務,有則從微任務隊列頭部開始逐一執行微任務中的同步代碼,如果該微任務中有宏任務,則將宏任務加到宏任務隊列的末尾,如果微任務中微任務,則將微任務加到微任務隊列尾部中,並在此次事件循環中執行完

微任務隊列中的任務都執行完后,繼續下一輪事件循環,從宏任務頭部取出第一個任務,執行同步代碼,之后重復3和4步

 

下面見一個例子:
    function a () { console.log(1) let macrotask1 = setTimeout(() =>{ console.log(2) let microtask4 = new Promise((resolve,reject) =>{ console.log(11) resolve() }).then(res =>{ console.log(12) }) console.log(13) }, 0) let microtask1 = new Promise((resolve,reject) =>{ console.log(3) resolve() }).then(res =>{ console.log(4) let microtask3 = new Promise((resolve,reject) =>{ console.log(14) resolve() }).then(res =>{ console.log(15) }) console.log(16) }) b() console.log(9) } function b (){ console.log(5) let macrotask2 = setTimeout(() =>{ console.log(6) }, 0) let microtask2 = new Promise((resolve,reject) =>{ console.log(7) resolve() }).then(res =>{ console.log(8) }) } a() console.log(10) // 1 3 5 7 9 10 4 14 16 8 15 2 11 13 12 6 // 

為了更好說明,microtask表示代碼中微任務,macrotask表示宏任務

 

將script宏任務放入調用棧,執行同步代碼,所以先打印1

接着遇到macrotask1(setTimeout,因為是0秒,所以立馬加入到宏任務隊列中)第一個宏任務,將其加入宏任務隊列的尾部

接着遇到microtask1(promise)第一個微任務,new Promise在實例的過程中執行代碼都是同步進行的,只有回調then()才是微任務,所以打印3,並將then加到了微任務隊列中

接着遇到調用b函數,所以先打印了5

接着遇到macrotask2(setTimeout)第二個宏任務,將其加到紅任務隊列的尾部

接着遇到microtask2(promise)第二個微任務,實例化執行同步代碼,打印7,並將then中打印8的代碼加到微任務隊列的尾部

至此b函數同步代碼執行完成,接着執行a函數中最后的同步代碼,打印9

至此a函數同步代碼執行完成,

接着執行script最后的同步代碼,打印10

至此第一輪事件循環的同步代碼執行完成

此時微任務隊列中有microtask1和microtask2兩個微任務,微任務隊列依次從前往后執行微任務

執行第一個microtask1微任務,打印4

接着又遇到了第三個微任務,執行實例化同步代碼,打印14,並將then中打印15的代碼加到微任務隊列的尾部,此時微任務隊列中有三個微任務

接着執行同步代碼console.log(16),打印16,至此第一個微任務執行完成,從微任務隊列中刪除

接着執行第二個microtask2微任務,打印8,至此第二個微任務執行完成,從微任務隊列中刪除

接着執行第三個microtask3微任務,打印15,至此第二個微任務執行完成,從微任務隊列中刪除

此時微任務隊列已經為空,第一輪事件循環執行完成,並從宏任務隊列中刪除

接着執行下一輪事件循環,取出第一個宏任務,並執行同步代碼,打印2

接着遇到microtask4(promise)第四個微任務,實例化執行同步代碼,打印11,並將then中打印12的代碼加到微任務隊列中,此時微任務隊列只有這個微任務

接着執行macrotask1宏任務中最后的同步代碼,打印13

接着查看微任務隊列,發現有一個微任務microtask4

接着執行微任務microtask4,打印12,至此該微任務執行完成,並從微任務隊列中刪除,此時微任務隊列為空,至此第二輪事件循環執行完成,並從宏任務隊列中刪除

接着執行下一輪事件循環,從宏任務隊列取出第一個宏任務,也就是macrotask2,執行該宏任務中同步代碼,打印6

 

佛山vi設計https://www.houdianzi.com/fsvi/ 豌豆資源搜索大全https://55wd.com

dom事件回調函數是宏任務,那么見下代碼:

console.log(1); new Promise((resolve, reject) => { resolve(3) }).then(() => { console.log(2); }) var button = document.querySelector(".button"); button.addEventListener('click', () => { console.log(3); }) button.click() // 1 3 2 

按照上面說的執行規則,按理說打印順序是 123,但運行的結果是1 3 2,那么問題出在哪里呢,那是因為人工合成(synthetic)的事件派發(dispatch)是同步執行的,包括執行click()和dispatchEvent()這兩種方式。直接 domEle.click() 和 真的在頁面上點擊然后觸發事件回調應該是不一樣的。所以上面的代碼執行順序就像下面的:

console.log(1); new Promise((resolve, reject) => { resolve(3) }).then(() => { console.log(2); }) console.log(3)

 

vue.nextTick的原理

vue nextTick其實就是將dom更新后的操作當成微任務加到dom更新微任務的后面,保證其執行的順序,再不行就使用setTimeout宏任務代替,在下一輪事件循環中執行,這也是為什么Promise,MutationObserver的優先級比setTimeout高


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM