美文网首页
Promise执行顺序

Promise执行顺序

作者: 薯条你哪里跑 | 来源:发表于2021-08-03 19:43 被阅读0次

    这段代码执行后,分别打印出来什么?

    let v = new Promise(resolve => {
          console.log("begin");
          resolve("then");
        });
    new Promise(resolve => {
          resolve(v);
        }).then((v)=>{
            console.log(v)
        }); 
    new Promise(resolve => {
          console.log(1);
          resolve();
        }) .then(() => {
            console.log(2);
        }).then(() => {
            console.log(3);
        }) .then(() => {
            console.log(4);
       })
    
    

    答案:

    begin
    1
    2
    3
    then
    4
    

    刚开始看这个问题的时候这个 then和4的顺序很让我迷惑。
    后来看了很多文章,这里resolve(v), v是一个promise属于thenable对象。

    new Promise(resolve => {
          // v是一个thenable
          resolve(v);
        }).then((v)=>{
            console.log(v)
    });
    //  执行起来相当于,及有两个then往后推了两个微任务。
    new Promise(resolve => {
          Promise.resolve().then(()=>{
                v.then(resolve)
          })
        }).then((v)=>{
            console.log(v)
    });
    
    
    var a = Promise.resolve(true)
    //same as
    var a = new Promise(function(resolve,reject){
        //立即变成成功状态
        resolve(true)
    })
    
    var b = Promise.reject(true)
    //same as
    var b = new Promise(function(resolve,reject){
        //立即变成失败状态
        reject(true)
    })
    
    在 Promise 中 resolve 一个 Promise 实例解析:
    • 由于 Promise实例是一个对象,其原型上有 then 方法,所以这也是一个 thenable 对象。
    • 同样的,浏览器会创建一个 PromiseResolveThenableJob 去处理这个 Promise 实例,这是一个微任务。
    • PromiseResolveThenableJob 执行中,执行了 Promise.prototype.then,而这时 Promise 如果已经是 resolved 状态 ,then 的执行会再一次创建了一个微任务
    最终结果就是:额外创建了两个Job,表现上就是后续代码被推迟了2个时序

    对于 new Promise(resolve => resolve(thenable)),即“在 Promise 中 resolve 一个 thenable 对象”,需要先将 thenable 转化为 Promsie,然后立即调用 thenable 的 then 方法,并且 这个过程需要作为一个 job 加入微任务队列,以保证对 then 方法的解析发生在其他上下文代码的解析之后。

    let thenable = {
      then(resolve, reject) {
        console.log('in thenable');
        resolve(100);
      }
    };
    
    new Promise((r) => {
      console.log('in p0');
      r(thenable);
    })
    .then(() => { console.log('thenable ok') })
    
    new Promise((r) => {
      console.log('in p1');
      r();
    })
    .then(() => { console.log('1') })
    .then(() => { console.log('2') })
    .then(() => { console.log('3') })
    .then(() => { console.log('4') });
    

    执行顺序:

    in p0
    in p1
    in thenable
    1
    thenable ok
    2
    3
    4
    

    解析:

    • in thenable 后于in p1 而先于1 输出,同时 thenable ok1 后输出
    • 在执行完同步任务后,微任务队列中只有2个微任务:第一个是 转换thenable为Promise的过程,即 PromiseResolveThenableJob,第二个是 console.log('1')
    • PromiseResolveThenableJob执行中会执行 thenable.then(),从而注册了另一个微任务:console.log('thenable ok')
    • 正是由于规范中对 thenable 的处理需要在一个微任务中完成,从而导致了第一个 Promise 的后续回调被延后了1个时序

    参考:令人费解的 async/await 执行顺序
    测试: https://segmentfault.com/a/1190000022805523

    https://segmentfault.com/a/1190000022805523

    再来一个await

    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')
    }, 0)
        
    async1();
        
    new Promise(function (resolve) {
        console.log('promise1')
        resolve()
    }).then(function () {
        console.log('promise2')
    })
        
    console.log('script end')
    
    

    答案:

    script start
    async1 start
    async2
    promise1
    script end
    async1 end
    promise2
    setTimeout
    

    梳理下await函数的执行时机,我们可以看看这篇文章:《「译」更快的 async 函数和 promises》,下面引入其中的一些描述:

    简单说,await v 初始化步骤有以下组成:

    1. 把 v 转成一个 promise(跟在 await 后面的)。
    2. 绑定处理函数用于后期恢复。
    3. 暂停 async 函数并返回 implicit_promise 给调用者。

    我们一步步来看,假设 await 后是一个 promise,且最终已完成状态的值是 42。然后,引擎会创建一个新的 promise 并且把 await 后的值作为 resolve 的值。借助标准里的 PromiseResolveThenableJob 这些 promise 会被放到下个周期执行。

    结合规范和这篇文章,简单总结一下,对于 await v:

    • await 后的值 v 会被转换为 Promise
    • 即使 v 是一个已经 fulfilled 的 Promise,还是会新建一个 Promise,并在这个新 Promise 中 resolve(v)
    • await v 后续的代码的执行类似于传入 then() 中的回调
      如此,可进一步对原题中的 async1 作等价转换。
    function async1(){
      console.log('async1 start')
      return new Promise((resolve,reject)=>{
          resolve(async2())
      }).then(() => {
          console.log('async1 end')
      });
    }
    

    即await下面的代码被拖到了微任务队列中。

    相关文章

      网友评论

          本文标题:Promise执行顺序

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