Promise相关知识

基础知识

event loop事件循环使得JS单线程可以并发的执行异步任务而无需额外创建线程。

JS中的异步任务分为宏任务和微任务。

宏任务:包括setTimeout、setInterval、I/O、UI渲染、事件处理等

微任务:包括Promise的回调(.then, .catch)、MutationObserver、process.nextTick(Node.js特有)等。

event loop的执行顺序

1.整个方法作为一个宏任务执行

2.执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列

3.当前宏任务执行完成后,检查微任务列表,有就依次执行

4.执行浏览器UI线程的渲染工作

5.执行完本轮的宏任务,回到2,循环直到宏任务和微任务都为空。

promise的.then .catch .finally的相关题目

promise.then的时候如果状态不是pending就会把它加入到微任务队列中,如果还是pending状态then不会执行,需要等到被改变状态后才被加入到微任务队列中。

题目一

const promise = new Promise((resolve, reject) => {
  resolve("success1");
  reject("error");
  resolve("success2");
});
promise
.then(res => {
    console.log("then: ", res);
  }).catch(err => {
    console.log("catch: ", err);
  })

output 
"then:success1"

promise的状态一经改变不能再改变

题目二

const promise = new Promise((resolve, reject) => {
  reject("error");
  resolve("success2");
});
promise
.then(res => {
    console.log("then1: ", res);
  }).then(res => {
    console.log("then2: ", res);
  }).catch(err => {
    console.log("catch: ", err);
  }).then(res => {
    console.log("then3: ", res);
  })

outout
"catch: " "error"
"then3: " undefined

catch不管被连接在哪里,都能捕获上层未捕获的错误。而then3被执行是因为catch()也返回一个promise,由于这个promise没有返回值,所以打印出来的是undefined

题目三

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

output
1
2

promise可以链式调用,不过proomise每次调用.then或者.catch都会返回一个新的promise,从而实现链式调用。

题目四

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('timer')
    resolve('success')
  }, 1000)
})
const start = Date.now();
promise.then(res => {
  console.log(res, Date.now() - start)
})
promise.then(res => {
  console.log(res, Date.now() - start)
})

output
'timer'
'success' 1001
'success' 1001

promise的.then或者.catch可以被调用多次,但是promise构造函数只执行一次,promise的内部状态一旦改变,并且有了一个值,后续每次.then或者.catch都会直接拿到这个值。

题目五

Promise.resolve().then(() => {
  return new Error('error!!!')
}).then(res => {
  console.log("then: ", res)
}).catch(err => {
  console.log("catch: ", err)
})

output
"then: " "Error: error!!!"

返回任意一个非 promise 的值都会被包裹成 promise 对象,因此这里的return new Error('error!!!')也被包裹成了return Promise.resolve(new Error('error!!!'))

如果你想抛出一个错误,可以用下面的方式

return Promise.reject(new Error('error!!!'));
// or
throw new Error('error!!!')

题目六

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

output
1

.then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传。第一个then和第二个then中传入的都不是函数,一个是数字类型,一个是对象类型,因此发生了透传,将resolve(1) 的值直接传到最后一个then里。

题目七

Promise.reject('err!!!')
  .then((res) => {
    console.log('success', res)
  }, (err) => {
    console.log('error', err)
  }).catch(err => {
    console.log('catch', err)
  })

'error' 'error!!!'

进入的是then()里的第二个参数里面,不会进入catch,如果把第二个参数去掉就进入catch了。

Promise.resolve()
  .then(function success (res) {
    throw new Error('error!!!')
  }, function fail1 (err) {
    console.log('fail1', err)
  }).catch(function fail2 (err) {
    console.log('fail2', err)
  })

fail2 Error: error!!!

但如果是在then里面抛出异常,会被catch捕获。

题目八

Promise.resolve('1')
  .then(res => {
    console.log(res)
  })
  .finally(() => {
    console.log('finally')
  })
Promise.resolve('2')
  .finally(() => {
    console.log('finally2')
  	return '我是finally2返回的值'
  })
  .then(res => {
    console.log('finally2后面的then函数', res)
  })

'1'
'finally2'
'finally'
'finally2后面的then函数' '2'

这两个Promise.finally都会执行,且就算finally2返回了新的值,它后面的then()函数接收到的结果却还是'2'。

如果在finally中抛出异常,会被catch捕获。

Promise.resolve('1')
  .finally(() => {
    console.log('finally1')
    throw new Error('我是finally中抛出的异常')
  })
  .then(res => {
    console.log('finally后面的then函数', res)
  })
  .catch(err => {
    console.log('捕获错误', err)
  })

'finally1'
'捕获错误' Error: 我是finally中抛出的异常

题目九

而promise的多个链式调用的执行时机又是如何的?需要等到前一个微任务执行完成后,才会继续执行后面的.then或者.finally操作。代码中标注了微任务在队列中的顺利,也就是执行到微任务1的时候,微任务3才进微任务队列,这个时候前面已经有微任务2了。

function promise1 () {
  let p = new Promise((resolve) => {
    console.log('promise1');
    resolve('1')
  })
  return p;
}
function promise2 () {
  return new Promise((resolve, reject) => {
    reject('error')
  })
}
promise1()
  .then(res => console.log(res)) // 微任务1
  .catch(err => console.log(err))
  .finally(() => console.log('finally1')) // 微任务3

promise2()
  .then(res => console.log(res)) 
  .catch(err => console.log(err)) // 微任务2
  .finally(() => console.log('finally2')) // 微任务4

'promise1'
'1'
'error'
'finally1'
'finally2'

promise的all和race

.all():接收一组异步任务并行执行,并在所有异步任务执行完成后才执行回调

.race():接收一组异步任务并行执行,只取第一个执行完成的异步操作的结果,其他的方法仍在执行,不过执行结果会被丢弃。

当一组异步任务中有返回错误的情况,.all()会如何执行?

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

all和race传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被then的第二个参数或者后面的catch捕获;但并不会影响数组中其它的异步任务的执行。

race的使用场景:我们可以用race给某个异步任务设置超时事件。

all的使用场景:进入页面并发下载页面所需要的初始化资源,等结果都返回后再刷新页面。

async/await

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
async1();
console.log('start')


output
'async1 start'
'async2'
'start'
'async1 end'

await后面的语句相当于放到一个promise中,下一行以及之后的语句相当于放在promise.then中。

async function async1() {
  console.log("async1 start");
  // 原来代码
  // await async2();
  // console.log("async1 end");
  
  // 转换后代码
  new Promise(resolve => {
    console.log("async2")
    resolve()
  }).then(res => console.log("async1 end"))
}

如果async直接返回一个值,会输出什么?

async function fn () {
  // return await 1234
  // 等同于
  return 123
}
fn().then(res => console.log(res))

123

正常是希望得到一个promise对象,如果不是就会直接返回对应的值,等价于promise.resolve() 

未完待续...

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值