最近又碰到了event loop问题,之前研究的实在是浅显(🔗https://www.cnblogs.com/zx0423/p/12641637.html)所以今天主要讲述promise的链式调用,async/await,requestAnimationFrame以及MutationObserver()。

1、Promise链式调用

 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
*/

解释:

// 行2 ,执行同步的executor函数
栈【 promise 】
宏任务队列【】
微任务队列【 callback1 】
执行结果:promise1

// 行7
栈【 callback1 】
宏任务队列【】
微任务队列【 callback2,callback3 】
执行结果:promise1 , then11 , promise2

//
栈【 callback2,callback3 】
宏任务队列【】
微任务队列【 callback4 】
执行结果:promise1 , then11 , promise2 , then21 , then12 ,

// 行16
栈【 callback4 】
宏任务队列【】
微任务队列【 】
执行结果:promise1 , then11 , promise2 , then21 , then12 , then23

⚠️ 当行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

    每次我们使用 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()

基本概念: Window.requestAnimationFrame(callback):告诉浏览器——希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画;
      MutationObserver():会在指定的DOM发生变化时被调用。【微任务,很好理解,按照正常的执行顺序执行即可】

 大家先来看一段代码吧,思考一下他的执行结果是什么?然后在刷新看看,结果还是不是一样的了?

<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');

如图:

EventLoop-浏览器篇2-LMLPHPEventLoop-浏览器篇2-LMLPHP

可以看到,requestAnimationFrame的执行顺序是不一致的,

  起初,我将requestAnimationFrame归到宏任务中,原因是它绝大多数都会在setTimeout回调执行之后才执行。并将这个结果解释为是由于浏览器在执行渲染的时候,每次执行的时间会有差异,所以导致requestAnimationFrame和setTimeout被压入调用栈的时机不一致,也就导致了回调的时间不一致。

但是,可以看到宏任务与raf任务有明显的差异:
  执行时机不同;
  raf任务队列被执行时,会将其此刻队列中所有任务都执行完,既不属于宏任务/微任务,同时还排在微任务之后。

而且,在一个loop中,可能并不会执行这一步,只要浏览器任务不需要执行渲染,就会跳过。所以会出现requestAnimationFrame顺序不一致的状况。

08-28 03:42