【前端面试之代码输出】异步、事件循环(promise、async)

主要针对面试中常考的代码输出题,对其进行汇总,并进行规律总结。
这部分知识还涉及到微任务以及宏任务的知识点。如果还不够了解,可以查看我的这篇博客
微任务和宏任务

题1(promise内部状态变化 )

const promise = new Promise((resolve, reject) => {
  console.log(1);
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);
输出:
1
2
4

在这里插入图片描述
解题:
1、首先定义new promise时,会输出1,2,但是因为promise此时依然处于pending状态,因此无法输出3,顺序执行然后是输出4.

总结:
1、promise.then是微任务,会在所有的宏任务执行完成之后才执行,同时需要promise内部的状态发生变化。
2、如果promise内部状态没有变化的话,就会一直处于pending状态。
3、开始状态由pending变为resolve,说明已经变为已完成状态,catch也不会捕获到错误。

题2(宏任务与微任务的运行顺序)

Promise.resolve().then(() => {
  console.log('promise1');
  const timer2 = setTimeout(() => {
    console.log('timer2')
  }, 0)
});
const timer1 = setTimeout(() => {
  console.log('timer1')
  Promise.resolve().then(() => {
    console.log('promise2')
  })
}, 0)
console.log('start');
输出:
start
promise1
timer1
promise2
timer2
代码执行过程:
1. Promise.resolve().then是一个微任务,加入微任务队列
2. timer1.宏任务,加入宏任务队列
3. 继续执行下面的代码,有console.log,打印`start`,第一轮宏任务执行完毕
4. 查看微任务队列,执行Promise.resolve().then,输出`promise1`,定时器time2加入宏任务队列
5. 这一轮微任务执行完毕。开始执行第二轮宏任务,首先是定时器timer1,打印`timer1`,将里面的Promise.resolve().then微任务加入到队列中。
6. 执行微任务队列中的`promise2`
7. 最后执行宏任务timer2定时器,打印`timer2`

题3(如果resolve是原始值,如果.then中传递不是一个函数会怎样?)

Promise.resolve方法的参数如果是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的Promise对象,状态为resolvedPromise.resolve方法的参数,会同时传给回调函数。
then()方法接受的参数是函数,而如果传递的并非是一个函数,它实际上会将其解释为then(null),这就会导致前一个Promise的结果会传递下面。

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

1

题4(如果.then中有返回值会怎样?)

promise是可以链式调用,每次的调用.then或者.catch都会返回一个新的promise。

Promise.resolve(1)
  .then(res => {
    console.log(res);
    return 2;
  })
  .catch(err => {
    return 3;
  })
  .then(res => {
    console.log(res);
  });
  输出:
  1
  2

resolve(1)之后走的是第一个then方法,没有进catch里面。因此会输出1
第二个then中的res得到的实际上时第一个then的返回值。并且return 2会包装为resolve(2),被最后的then打印输出2

题5.finally()

finally()一般用的很少,只要记住以下几点就可以了:
● .finally()方法不管Promise对象最后的状态如何都会执行
● .finally()方法的回调函数不接受任何的参数,也就是说你在.finally()函数中是无法知道Promise最终的状态是resolved还是rejected的
● 它最终返回的默认会是一个上一次的Promise对象值,不过如果抛出的是一个异常则返回异常的Promise对象。
● finally本质上是then方法的特例

题6 Promise.all()

function runAsync (x) {
    const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
    return p
}
Promise.all([runAsync(1), runAsync(2), runAsync(3)]).then(res => console.log(res))
1
2
3
[1, 2, 3]
function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
function runReject (x) {
  const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
  return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
       .then(res => console.log(res))
       .catch(err => console.log(err))
// 1s后输出
1
3
// 2s后输出
2
Error: 2
// 4s后输出
4
这两个都用到了Promise.all来检测所有的异步对象的变化,第一个最后.then输出的是一个数组。
第二个由于有错误。而一组异步操作中有一个异常都不会进入.then()的第一个回调函数中。

题7 Promise.race()方法

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log('result: ', res))
  .catch(err => console.log(err))

输出:
1
'result: ' 1
2
3
Promise.race后面的.then只会捕获第一个成功的方法,其他的函数虽然还会继续执行,但是不会被then捕获了。
all和race传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被then的第二个参数或者后面的catch捕获;但并不会影响数组中其它的异步任务的执行。

题8 async await

async function async1 () {
  await async2();
  console.log('async1');
  return 'async1 success'
}
async function async2 () {
  return new Promise((resolve, reject) => {
    console.log('async2')
    reject('error')
  })
}
async1().then(res => console.log(res))
输出:
async2
Uncaught (in promise) error
可以看到,如果async函数中抛出了错误,就会终止错误结果,不会继续向下执行。
如果想要让错误不足之处后面的代码执行,可以使用catch来捕获。

题9 async await

const async1 = async () => {
  console.log('async1');
  setTimeout(() => {
    console.log('timer1')
  }, 2000)
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 end')//没有执行
  return 'async1 success'//没有执行
} 
console.log('script start');
async1().then(res => console.log(res));//没有执行
console.log('script end');
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .catch(4)
  .then(res => console.log(res))
setTimeout(() => {
  console.log('timer2')
}, 1000)

输出:
script start
async1
promise1
script end
1
timer2
timer1
 await后面的函数,如果没有返回值的话,后面的代码不会再执行。这一点跟Promise.then不一样,Promise.then就算上一步没有返回值,这也会继续执行,只不过状态码一直是pending

规律总结

  • promise.then是微任务,在所有的宏任务执行完成之后执行。
  • 如果promise内部的状态没有发生变化的话,就会一直处于pending状态。
  • 当开始状态由pending变成resolve,说明状态已经已完成,catch不会再捕获到错误。
  • Promise.resolve方法的参数如果是一个原始值,或者是一个不具有then方法的对象,则会返回一个新的Promise对象,状态为resolved,同时会叫resolve方法的参数传递给回调函数。
  • then()方法中传递的不是一个函数的话,会将其解释为then(null),导致前一个Promise结果会传递到下面。
  • 每次调用.then或者.catch都会返回一个新的promise。
  • all和race传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被then的第二个参数或者后面的catch捕获;但并不会影响数组中其它的异步任务的执行。
  • 如果async函数中抛出了错误,就会终止错误结果,不会继续向下执行。如果想要让错误不足之处后面的代码执行,可以使用catch来捕获。
  • await后面的函数,如果没有返回值的话,后面的代码不会再执行。这一点跟Promise.then不一样,Promise.then就算上一步没有返回值,这也会继续执行,只不过状态码一直是pending。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值