JS异步代码执行

在面试题中,考察异步的面试题最多的就是Promise和setTimeout的执行顺序问题

首先,一个大前提需要知道,Promise是进微队列,setTimeout是进宏队列。

其次,要知道,Promise的三种状态,以及如何变化的

三种状态

  • pending (待定)

  • fulfilled(resolved)(成功)

  • rejected (失败)

变化

实例的初始状态是pending,一旦由pending状态变为fulfilled状态或rejected状态,就不可再次改变。

所以与之对应的,Promise类实例对象有 成功结果 和 失败结果,默认值都是undefined,只有状态被改变后,才会为其赋值。

在实例化一个Promise时会传入一个执行器函数进行操作,它接收2个参数:resolve和reject,都是回调函数,用来落定状态的。 执行器函数内部可以根据操作的情况通过两个回调来决定实例的状态是fulfilled还是rejected。

通过一个小例子先了解一下执行顺序

console.log(100)
setTimeout(() =>{
    console.log(200) // 宏任务
})
Promise.resolve().then(() =>{
    console.log(300) // 微任务
})
console.log(400)

// 100 400 300 200
//执行顺序:同步任务 → 微任务 → 宏任务
以上的执行顺序,就是 100 , 400, 300, 200.
因为首先输出100,接着遇到setTimeout后将其加入宏队列,然后Promise加入微队列,继续执行输出400。同步任务执行完毕后,先从微队列取出Promise,resolve()返回一个状态为resolved的Promise对象,执行then回调输出300,此时微队列也没有任务了,进而去宏队列里找到setTimeout进行输出

上面一道题就是较为简单的setTimeout和Promise混合的顺序问题,接着看稍微难一丢丢的Promise题

Promise.resolve().then(() =>{
    console.log(1) // 返回状态为 fulfilled 的 Promise 对象,下一步执行 then
}).then(() =>{
    console.log(2) // 返回状态为 fulfilled 的 Promise 对象,下一步执行 then
}).catch(() =>{
    console.log(3) // 不执行
})
// 1 2
Promise.resolve().then(() =>{console.log(1) // 返回状态为 fulfilled 的 Promise 对象,下一步执行 then}) 执行完这里,返回的是一个状态为resolved的Promise对象,依旧执行then,也依旧是resolved状态。所以不会执行catch (这个是针对rejected的Promise回调)

const promise = new Promise((resolve,reject)=>{
    console.log(1);
    resolve();
    console.log(2);
    reject()
})
setTimeout(()=>{console.log(5)},0)
promise.then(()=>{console.log(3)})
.then(()=>{console.log(6)})
.catch(()=>{console.log(7)})
console.log(4)
答案是 : 1 2 4 3 6 5
执行顺序依次是:
首先会定义一个promise,new Promise实例时会立即执行传入的方法。所以依次会输出1 和 2。而遇到的resolve()会将状态改为resolved,但是reject并不奏效,因为状态改变不可逆
然后遇到了setTimeout,直接先加进宏队列中,因为promise是resolved状态,所以会执行then回调,不过也会将回调任务加到微队列里。 所以继续执行同步任务,输出4。
同步任务至此执行完毕,先从微队列找,里面有一个then回调,输出3,返回resolved的Promise对象,继续执行then,输出6。 至于catch回调因为状态不是rejected所以压根不会执行。 至此,微队列为空,再去宏队列执行setTimeout输出5.

这道题比上一道又稍稍复杂一点

const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve();
        }, 0);
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });
}));
first().then((arg) => {
    console.log(arg);
});
console.log(4);
答案:3 7 4 1 2 5
首先依旧是先定义first()函数,执行它会new Promise(这里称为外部Promise),所以立即执行传入的方法,就先输出3。 然后又new了一个Promise(这里称为内部Promise,p),也是立即执行传入方法,输出7。遇到了setTimeout,加入宏队列,先不管他。 resolve(1),这里是改变的内部Promise的状态为resolved,且值变为1.而resolve(2)才是改变外部Promise状态为resolved,且值为2(这个要区分清楚。 然后p.then(),输出内部Promise的值,2. first().then输出外部Promise的值1.
此时,微队列空,宏队列里有setTimeout,输出5.

到这里应该基本的顺序问题都能解出来了,用两道简单题检验一下

console.log(1);
new Promise(resolve => {
    resolve();
    console.log(2);
}).then(() => {
    console.log(3);
})
setTimeout(() => {
    console.log(4);
}, 0);
console.log(5);
答案: 1 2 5 3 4

Promise.resolve()
.then(() => {
    console.log('1');
})
.then(() => {
    console.log('2');
});


setTimeout(() => {
    Promise.resolve()
    .then(() => {
        console.log('3');
    })
    .then(() => {
        console.log('4');
    });
    setInterval(() => {
        console.log('5');
    }, 3000);
    console.log('6');
}, 0);
答案: 1 2 6 3 4 5 5 5 5...
首先执行Promise。resolve,将两个回调任务加入微队列中。 执行setTimeout,将其加入宏队列。
调用栈为空, 拿出微队列的两个回调执行,依次输出1、2. 微队列为空,宏队列中取出setTimeout回调,将Promise.resolve的两个回调加入微队列中,继续往下执行setInterval的回调放到宏队列,执行输出6。 此时setTimeout任务完成,取出微队列两个Promise回调,输出3、4。 再宏队列的setInterval,每3秒输出5.
PS:这里setInterval调用时,不是马上执行的,第一次执行是3s后

之后还有几道async/await promise setTimeout混合的,到时候再更新吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值