EventLoop-瀏覽器篇2
- 2020 年 8 月 27 日
- 筆記
- javascript
最近又碰到了event loop問題,之前研究的實在是淺顯(🔗//www.cnblogs.com/zx0423/p/12641637.html)所以今天主要講述promise的鏈式調用,async/await,
1 new Promise((resolve,reject)=>{ 3 console.log("promise1"); 4 resolve(); 5 }).then(()=>{ 6 //callback1 7 console.log("then11"); 8 new Promise((resolve,reject)=>{ 9 console.log("promise2"); 10 resolve(); 11 }).then(()=>{ 12 // callback2 13 console.log("then21"); 14 }).then(()=>{ 15 // callback4 16 console.log("then23"); 17 }) 18 }).then(()=>{ 19 // callback3 20 console.log("then12"); 21 })
/*
1、new promsie是同步程式碼,立即執行
2、外部promise第一個then需要等外部promise被resolve後才執行,外部promise立即被resolve,於是執行註冊的 then回調。此時外部第二then並未執行,因為他等待的是外部第一個then返回的promise
3、進入外部第一個then,回調裡面是一個promise,執行內部promise時,同樣兩個then鏈式調用,內部第一個then執 行後,內部promise算執行完,返回一個新的promise給內部第二then用
4、外部第一個then執行完且有返回值,進入外部第二then
5、之後執行內部第二then
*/
解釋:
⚠️ 當行8添加 return 語句的時候,答案就截然不同了,大家可以自行體會一下。
2、async/await
-
async
-
返回一個 Promise 對象
1 async function test() { 2 return 1; // async的函數 在這裡隱式 使用 Promise.resolve(1) 3 } 4 5 //===>等價於 6 new Promise(function(resolve, reject) { 7 resolve(1); 8 })
-
每次我們使用 await, 解釋器都創建一個 promise 對象,然後把剩下的 async 函數中的操作放到 then 回調函數中。async/await 的實現,離不開 Promise。
1-await後面不是Promise對象,即是一些直接量 / 數值之類的
-
await會阻塞後面的程式碼,
-
先從右到左執行,
-
再執行async外面的同步程式碼,
-
同步程式碼執行完,再回到async內部,
-
把這個非promise的東西,作為 await表達式的結果。
-
2-await後面是Promise對象
-
await 它會「阻塞」後面的程式碼,
-
先從右到左執行,
-
再執行async外面的同步程式碼,
-
【期間不管宏任務微任務,就一直執行,直到…】
-
直到 Promise 對象 resolved(也就是要等到了結果),即把 resolve 的 參數/值 作為 await 表達式的運算結果。
-
理清上述執行順序,對下面這道題就可以迎刃而解了。
-
1 // async/await 與 then鏈式調用 2 async function f1(){ 3 console.log("1"); 4 await 'await的結果' 5 console.log("3") 6 } 7 8 f1(); 9 10 new Promise(function(resolve){ 11 console.log("2") 12 resolve(); 13 }).then(function(result){ 14 new Promise( r => r()).then(() =>{ 15 console.log(4) 16 }).then(()=>{ 17 console.log(5); 18 }) 19 })
結果: 1,2,3,4,5
3、requestAnimationFrame & MutationObserver()
大家先來看一段程式碼吧,思考一下他的執行結果是什麼?然後在刷新看看,結果還是不是一樣的了?
<div id="testElement"></div>
1 setTimeout(() => { 2 console.log(performance.now(), 'settimeout'); 3 }, 0) 4 5 requestAnimationFrame(() => {
console.log(performance.now(),'requestAnimationFrame'); 6 }) 7 8 const testElement = document.getElementById('testElement'); 9 10 var observer = new MutationObserver(() => { 11 console.log('MutationObserver'); 12 }); 13 14 observer.observe(testElement, { 15 childList: true 16 }) 17 //添加子節點,觸發MutationObserver() 18 const div = document.createElement('div'); 19 testElement.appendChild(div); 20 21 new Promise(resolve => { 22 console.log('promise'); 23 resolve(); 24 }).then(() => console.log('then')) 25 26 console.log(performance.now(), 'global');
如圖:
可以看到,requestAnimationFrame的執行順序是不一致的,
起初,我將requestAnimationFrame歸到宏任務中,原因是它絕大多數都會在setTimeout回調執行之後才執行。並將這個結果解釋為是由於瀏覽器在執行渲染的時候,每次執行的時間會有差異,所以導致requestAnimationFrame和setTimeout被壓入調用棧的時機不一致,也就導致了回調的時間不一致。
但是,可以看到宏任務與raf任務有明顯的差異:
執行時機不同;
raf任務隊列被執行時,會將其此刻隊列中所有任務都執行完,既不屬於宏任務/微任務,同時還排在微任務之後。
而且,在一個loop中,可能並不會執行這一步,只要瀏覽器任務不需要執行渲染,就會跳過。所以會出現requestAnimationFrame順序不一致的狀況。