事件循环:Promise的一些细节

53 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情

前言

学习浏览器环境的事件循环机制离不开 Promise,最近在看相关内容的时候发现两道Promise相关的非常考察细节的面试题。

关键点:return Promise.resolve() 会注册2个空的微任务,可以理解为1个是用于判断return的Promise对象的状态,1个是状态判断后自动注册的空微任务

题1

Promise.resolve()
  .then(() => { // 设为 thenA
    console.log(0);
    return Promise.resolve().then(()=>{console.log('return后的then')}
    return Promise.resolve(4); // 重/难 点
  })
  .then((res) => { // // 设为 thenB
    console.log(res);
  });
Promise.resolve()
  .then(() => { // 设为 thenC
    console.log(1);
  })
  .then(() => { // 设为 thenD
    console.log(2);
  })
  .then(() => { // 设为 thenE
    console.log(3);
  })
  .then(() => { // 设为 thenF
    console.log(5);
  })
  .then(() => { // 设为 thenG
    console.log(6);
  });

执行结果:0、1、2、3、4、5、6

执行步骤分析:

设 浏览器微任务队列为:microtaskQueue = 【】

  1. 第一个Promise.resolve():注册 thenA 任务,加入微任务队列。此时 microtaskQueue = 【thenA
  2. 第二个Promise.resolve(): 注册 thenC 任务,加入微任务队列。此时 microtaskQueue = 【thenA, thenC
  3. 执行栈中无任务运行,按入队顺序执行微任务队列的任务:thenA()thenC()
  4. 执行 thenA()输出 0,执行到 return Promise.resolve(),此时还不会注册 thenB 任务。return Promise.resolve()后没有定义 then 方法,那么此时会以 resolve([value])value 值(此处为 undefined) 注册一个空的微任务,并加入队列。此时 microtaskQueue = 【thenC, thenEmpty
  5. 执行 thenC()输出 1,注册 thenD。此时 microtaskQueue = 【thenEmpty, thenD
  6. 执行 thenEmpty():会 自动注册一个空的微任务,此时 microtaskQueue = 【thenD, thenEmpty2
  7. 执行 thenD()输出 2,注册 thenE。此时 microtaskQueue = 【thenEmpty2, thenE
  8. 执行 thenEmpty2():空任务,无代码执行。此时 return Promise.resolve() 语句执行完毕,注册 thenB。此时 microtaskQueue = 【thenE, thenB
  9. 执行 thenE()输出 3,注册 thenF。此时 microtaskQueue = 【thenB, thenF
  10. 执行 thenB(res)输出 4res 接收的是 thenA 方法中 resolve的值。此时 microtaskQueue = 【thenF
  11. 执行 thenF()输出 5,注册 thenG。此时 microtaskQueue = 【thenG
  12. 执行 thenG()输出 6

巩固一下


// 原题是这样的:
async function async1() {
    await async2();
    console.log('async1 end');
}

async function async2() {
    console.log('async2 end');
    return Promise.resolve().then(() => {
        console.log('async2 end1');
    })
}

// async await 是语法糖,也可以改写成下面这样

function async1() {
    return new Promise((resolve, reject) => {
        async2().then(() => {
            console.log('async1 end');
        })
    })
}

function async2() {
    return new Promise((resolve, reject) => {
        console.log('async2 end');
        resolve(Promise.resolve().then(() => {
            console.log('async2 end1');
        }))
    })
}

async1();

new Promise(resolve => {
    console.log('Promise');
    resolve();
}).then(function() {
    console.log('promise1');
}).then(function() {
    console.log('promise2')
}).then(function() {
    console.log('promise3')
}).then(function() {
    console.log('promise4')
})

关键点:

  • 执行 return Promise.resolve() ,创建一个 Promise 实例,将 Promise 实例设置为 resolve 状态,这个 Promise.resolve() 是同步的,且该 Promise 已经完成了,并不会影响到其他 then 的注册
  • return Promise.resolve() 会注册 2 个空的微任务,可以理解为 1 个是用于判断 returnPromise 对象的状态,1个是状态判断后自动注册的空微任务

参考资料

下面两篇文章都是从V8源码层面对这类问题的讲解,值得一看(评论区也非常值得一看👍)

juejin.cn/post/684490…

juejin.cn/post/695345…