美文网首页
EventLoop - 遇上Promose

EventLoop - 遇上Promose

作者: 木头就是我呀 | 来源:发表于2020-04-07 23:43 被阅读0次

    当EventLoop遇上Promise后,会有一些弯绕不过来,以下题目作为参考,题目参考自https://juejin.im/post/5c9a43175188252d876e5903,特整理一版,以便翻阅。

    1. 得心应手版

    考点:EventLoop中的执行顺序,宏任务 / 微任务的区别。

    setTimeout(()=>{
       console.log(1) 
    },0)
    Promise.resolve().then(()=>{
       console.log(2) 
    })
    console.log(3) 
    
    // 输出 3 2 1  
    //如果不会这个  去网上重新学习吧
    

    这里就有一个版本问题,是参考的笔者所指出的,这也是我看了一些文章后疑惑的点,被笔者讲清楚了,很赞,贴出来,防止以后忘记。
    有时候会有版本是宏任务 ->微任务 -> 宏任务,在这里笔者需要讲清楚一个概念,以免混淆。这里有个main script(主代码)的概念,就是一开始执行的代码(代码总要有开始执行的时候对吧,不然宏任务和微任务的队列哪里来的),这里被定义为了宏任务(笔者喜欢将main script的概念单独拎出来,不和两个任务队列混在一起),然后根据main script中产生的微任务队列和宏任务队列,分别清空,这个时候是先清空微任务的队列,再去清空宏任务的队列。

    2. 游刃有余版

    setTimeout(()=>{ // s1
        console.log(1)
    },0)
    let a=new Promise((resolve)=>{ 
        console.log(2)
        resolve()
    }).then(()=>{ // p1
        console.log(3)
    }).then(()=>{ // p2
        console.log(4)
    })
    console.log(5)
    
    // 输出 2 5 3 4 1
    
    步骤:
    1. 主代码执行
    宏任务队列 = [s1]
    console.log(2)  ======================> 2
    微任务队列 = [p1]
    console.log(5)  ======================> 5
    
    2. 清空微任务
    微任务队列 = []
    console.log(3)  ======================> 3
    微任务队列 = [p2]
    
    3. 清空微任务
    微任务队列 = []
    console.log(4)  ======================> 4
    
    4. 拿出第一个宏任务 s1
    宏任务队列 = []
    console.log(1)  ======================> 1
    

    注意:
    这个要从Promise的实现来说,Promise的executor是一个同步函数,即非异步,立即执行的一个函数,因此他应该是和当前的任务一起执行的。而Promise的链式调用then,每次都会在内部生成一个新的Promise,然后执行then,在执行的过程中不断向微任务(microtask)推入新的函数,因此直至微任务(microtask)的队列清空后才会执行下一波的macrotask。

    3. 炉火纯青版

    这一个版本是上一个版本的进化版本,上一个版本的promise的then函数并未返回一个promise,如果在promise的then中创建一个promise,那么结果该如何呢?

    new Promise((resolve,reject)=>{
        console.log("promise1")
        resolve()
    }).then(()=>{  // p1
        console.log("then11")
        new Promise((resolve,reject)=>{ 
            console.log("promise2")
            resolve()
        }).then(()=>{ // p2
            console.log("then21")
        }).then(()=>{ // p3
            console.log("then23")
        })
    }).then(()=>{ // p4
        console.log("then12")
    })
    
    // 输出  
    // promise1
    // then11
    // promise2
    // then21
    // then12
    // then23
    
    步骤
    1. 主代码执行
    console.log("promise1")  ======================> promise1
    微任务队列 = [p1]
    
    2. 清空微任务队列
    微任务队列 = []
    console.log("then11")  ======================> then11
    console.log("promise2")  ======================> promise2
    微任务队列 = [p2 , p4]
    
    3. 清空微任务队列
    微任务队列 = [p4]
    console.log("then21")  ======================> then21
    微任务队列 = []
    console.log("then12")  ======================> then12
    微任务队列 = [p3]
    
    4. 清空微任务队列
    微任务队列 = []
    console.log("then23")  ======================> then23
    

    3.1 变异版本

    new Promise((resolve,reject)=>{
      console.log("promise1")
      resolve()
    }).then(()=>{ // p1
      console.log("then11")
      return new Promise((resolve,reject)=>{
          console.log("promise2")
          resolve()
      }).then(()=>{ // p2
          console.log("then21")
      }).then(()=>{ // p3
          console.log("then23")
      })
    }).then(()=>{ // p4
      console.log("then12")
    })
    
    // 输出  
    // promise1
    // then11
    // promise2
    // then21
    // then23
    // then12
    
    注意  return的Promise后,最外层的Promise的第二个then相当于挂在内部返回的Promise的最后一个then的返回值上。
    

    3.2 变异版本2

    new Promise((resolve,reject)=>{
        console.log("promise1")
        resolve()
    }).then(()=>{ // p1
        console.log("then11")
        new Promise((resolve,reject)=>{
            console.log("promise2")
            resolve()
        }).then(()=>{ // p2
            console.log("then21")
        }).then(()=>{ // p3
            console.log("then23")
       })
    }).then(()=>{ // p4
        console.log("then12")
    })
    new Promise((resolve,reject)=>{
        console.log("promise3")
        resolve()
    }).then(()=>{ // p5
        console.log("then31")
    })
    
    // 输出
    // promise1
    // promise3
    // then11
    // promise2
    // then31
    // then21
    // then12
    // then23
    
    步骤:
    1. 执行主代码
    console.log("promise1")  ======================> promise1
    console.log("promise3")  ======================> promise3
    微任务队列 = [p1 , p5]
    
    2. 清空微任务队列
    微任务队列 = [p5]
    console.log("then11")  ======================> then11
    console.log("promise2")  ======================> promise2
    微任务队列 = [p5 , p2]
    微任务队列 = [p2 , p4]
    console.log("then31")  ======================> then31
    微任务队列 = [p4 , p3]
    console.log("then21")  ======================> then21
    微任务队列 = [p3]
    console.log("then12")  ======================> then12
    微任务队列 = []
    console.log("then23")  ======================> then23
    

    4. 登峰造极版

    考点:在async/await之下,对Eventloop的影响。

    async function async1() {
        console.log("async1 start");
        await  async2();
        console.log("async1 end"); // async  (await返回一个promise,其后面的部分可以看做是被promise.then包裹)
    }
    
    async  function async2() {
        console.log( 'async2');
    }
    
    console.log("script start");
    
    setTimeout(function () { // s1
        console.log("settimeout");
    },0);
    
    async1();
    
    new Promise(function (resolve) {
        console.log("promise1");
        resolve();
    }).then(function () { // p1
        console.log("promise2");
    });
    console.log('script end');
    
    // 输出
    // script start
    // async1 start
    // async2
    // promise1
    // script end
    // async1 end
    // promise2
    // settimeout
    
    步骤:
    1. 执行主代码
    console.log("script start");  ======================> script start
    宏任务队列 = [s1]
    console.log("async1 start");  ======================> async1 start
    console.log( 'async2');  ======================> async2
    微任务队列 = [async]
    console.log("promise1");  ======================> promise1
    微任务队列 = [async , p1]
    console.log('script end');  ======================> script end
    
    2. 清空微任务队列
    微任务队列 = [p1]
    console.log("async1 end");  ======================> async1 end
    微任务队列 = []
    console.log("promise2");  ======================> promise2
    
    3. 拿出宏任务队列中的第一项 s1
    宏任务队列 = []
    console.log("settimeout");  ======================> settimeout
    该宏任务执行完毕
    

    async/await仅仅影响的是函数内的执行,而不会影响到函数体外的执行顺序。也就是说async1()并不会阻塞后续程序的执行,await async2()相当于一个Promise,console.log("async1 end");相当于前方Promise的then之后执行的函数。

    4.1 async/await与promise的优先级详解

    async function async1() {
        console.log("async1 start");
        await  async2();
        console.log("async1 end"); // async
    }
    async  function async2() {
        console.log( 'async2');
    }
    async1();
    // 用于test的promise,看看await究竟在何时执行
    new Promise(function (resolve) {
        console.log("promise1");
        resolve();
    }).then(function () { // p1
        console.log("promise2");
    }).then(function () { // p2
        console.log("promise3");
    }).then(function () { // p3
        console.log("promise4");
    }).then(function () { // p4
        console.log("promise5");
    });
    
    // 输出
    // async1 start
    // async2
    // promise1
    // async1 end
    // promise2
    // promise3
    // promise4
    // promise5
    
    步骤:
    1. 执行主代码
    console.log("async1 start");  ======================> async1 start
    console.log( 'async2');  ======================> async2
    微任务队列 = [async]
    console.log("promise1");  ======================> promise1
    微任务队列 = [async , p1]
    
    2. 清空微任务队列
    微任务队列 = [p1]
    console.log("async1 end");   ======================> async1 end
    微任务队列 = []
    console.log("promise2");  ======================> promise2
    微任务队列 = [p2]
    微任务队列 = []
    console.log("promise3");  ======================> promise3
    微任务队列 = [p3]
    console.log("promise4");  ======================> promise4
    微任务队列 = [p4]
    console.log("promise5");  ======================> promise5
    

    5. 究极变态版

    async function async1() {
        console.log("async1 start");
        await  async2();
        console.log("async1 end");
    }
    async  function async2() {
        console.log( 'async2');
    }
    console.log("script start");
    setTimeout(function () {
        console.log("settimeout");
    });
    async1()
    new Promise(function (resolve) {
        console.log("promise1");
        resolve();
    }).then(function () {
        console.log("promise2");
    });
    setImmediate(()=>{
        console.log("setImmediate")
    })
    process.nextTick(()=>{
        console.log("process")
    })
    console.log('script end');
    
    第一轮:
    
    current task:"script start","async1 start",'async2',"promise1",“script end”
    micro task queue:[async,promise.then,process]
    macro task queue:[setTimeout,setImmediate]
    
    第二轮
    
    current task:process,async1 end ,promise.then
    micro task queue:[]
    macro task queue:[setTimeout,setImmediate]
    
    
    第三轮
    
    current task:setTimeout,setImmediate
    micro task queue:[]
    macro task queue:[]
    
    最终结果:[script start,async1 start,async2,promise1,script end,process,async1 end,promise2,setTimeout,setImmediate]
    同样"async1 end","promise2"之间的优先级,因平台而异。
    

    * 干货总结

    在处理一段EventLoop的时候

    • 第一步确认宏任务,微任务
      • 宏任务:script,setTimeout,setImmediate,promise中的executor
      • 微任务:promise.then,process.nextTick
    • 第二步解析“拦路虎”,出现async/await的时候不要慌,他们只是在标记的函数中作威作福,出了这个函数还是跟着大部队的潮流。
    • 第三步,根据Promise的then调用方式的不同做出不同的判断,是链式调用还是分别调用。
    • 最后一步,记住一些特别事件:
      • 比如:process.nextTick优先级高于Promise.then

    本文是手抄自该文章https://juejin.im/post/5c9a43175188252d876e5903,对题目做了自己的步骤备注,感谢作者!

    相关文章

      网友评论

          本文标题:EventLoop - 遇上Promose

          本文链接:https://www.haomeiwen.com/subject/bkznphtx.html