1. async
1.1 async可以修饰函数表达式、函数声明、箭头函数和方法
let func = async function(){} //函数表达式
async function foo(){} //函数声明
async () => {} //箭头函数
class Person{
async methodA(){} //方法
}
1.2 async里面的代码还是同步执行的
async function foo(){
console.log(2)
return 3
}
foo().then((val) => {
console.log(val)
})
console.log(1)
/**
* 输出:
* 2
* 1
* 3
**/
1.3 async修饰的函数返回值不同场景
async修饰的函数返回值期望是实现thenable的接口的对象,但是常规值也可以,如果返回的是实现了thenable接口的对象,那么这个对象可以通过then进行解包输出;如果是常规值,那么会返回一个解决的期约
①没有返回值,会返回一个Promise.resolve(undefined)的期约
async function foo(){
console.log(2)
}
console.log(foo()) //Promise { undefined }
②如果有返回值a,会返回一个Promise.resolve(a)的期约
async function foo(){
console.log(2)
return 3
}
console.log(foo())//Promise { 3 }
③抛出异常:throw ‘err’,会返回一个Promise.reject(‘err’)的期约
async function foo(){
throw 'err'
}
console.log(foo())//Promise { <rejected> 'err' }
④函数内部执行Promise.reject(‘test’),但是这个拒绝期约不返回,那么返回的仍然是Promise.resolve(undefined)的期约(很容易理解,不返回值默认就是返回不包装任何值的期约),并且异步函数里面的拒绝期约无法捕获
async function foo(){
Promise.reject('a')
}
console.log(foo())//Promise { undefined }
foo().catch(console.log)//无法捕获,异常照样抛出
⑤异步函数返回一个拒绝期约,那理所当然会接收到一个拒绝期约,可以用catch捕获
async function foo(){
return Promise.reject('a')
}
let p = foo()
setTimeout(() => {
console.log(p) //Promise { <rejected> 'a' }
});
foo().catch(console.log)//解包输出:1
⑥异步函数返回resolve的期约,自然也是接收一个resolve的期约:
async function foo(){
return Promise.resolve('a')
}
let p = foo()
setTimeout(() => {
console.log(p) //Promise { 'a' }
});
foo().then(console.log)//解包输出:a
⑦返回一个实现thenable接口的对象
async function foo(){
const thenable = {then(callback){
callback('baz')
}}
return thenable
}
let p = foo()
setTimeout(() => {
console.log(p) //Promise { 'baz' }
});
foo().then(console.log)//解包输出:baz
2. await
2.1 await只能在async函数中使用,它会暂停async函数中后面代码的执行,等待await后面的代码执行完
async function foo(){
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('xixi')
resolve(3)
}, 1000);
})
console.log(await p)
console.log('haha')
}
foo()
//输出:
xixi
3
haha
上面的例子,如果不使用await,p会输出一个pending的期约,然后会先执行setTimeout后面的代码:'haha’的输出,最后再输出xixi , 执行结果是:
Promise { }
haha
xixi
2.2 await后面期望是一个实现thenable接口的对象,也可以是常规的值
await后面期望是一个实现thenable接口的对象,也可以是常规的值,如果是常规值,那么这个值会被当成一个已经解决的期约。如果实现了thenable接口,await会对对象进行“解包”,“解包”可以理解为解开需要then()执行才能拿到值的包装,如果是Promise.resolve(xxx),解包操作就是直接获取到xxx,如果Promise.reject(3),解包操作就是直接把reject的异常同步抛出(可以try catch获取,如果没有await,reject的期约是无法try…catch捕获的,只能通过then()解包)
① await 一个promise
//1、resolve的promise
async function foo(){
let p = Promise.resolve(3)
console.log(await p)//3
}
foo()
//2、reject的promise
async function foo(){
let p = Promise.reject(3)
try{
await p
}catch(e){
console.log(e) //3
}
}
foo()
② await一个普通的实现了thenable接口的对象
async function baz(){
const thenable = {then(callback){
callback('haha')
}}
console.log(await thenable)
}
baz()//输出:haha
③await一个常规值
async function foo(){
console.log(await 3)
}
foo() // 输出:3
④await 一个throw error,因为throw error在async中实际上是返回一个reject的期约的,所以等价于await一个拒绝的期约
async function foo(){
try{
await(() => {throw 3})()
}catch(e){
console.log(e)//输出:3
}
}
foo()
2.3 await对代码执行顺序的影响
异步函数中遇到await,会做一个标记,暂停异步函数的执行,等待await后面的值执行,在等待的过程中,如果有同步代码没有执行完,会先执行完同步代码,另一边,await后面的值如果可用了,js程序会把await后面的值放到消息队列,等同步代码执行完后,js程序从消息队列中拿await后面的值,然后继续执行异步函数后面的代码,举例:
async function bar(){
console.log(4)
console.log(await 6)
console.log(7)
}
console.log(1)
bar()
console.log(3)
输出:
1
4
3
6
7
3、错误处理
参考:https://cloud.tencent.com/developer/article/1470715
因为await返回的是一个promise,所以每一步await都有可能出错(返回reject的promise),为了捕获这些错误,我们使用try。。。catch。。
async function getUserInfo (cb) {
try {
const id = await request.getCurrentId()
} catch(e) {
return cb('an error in getCurrentId')
}
try {
const info = await request.getUserInfo(id)
} catch(e) {
return cb('an error in getUserInfo')
}
return cb(null, info)
}
这样如果一个异步函数中有很多await,我们每一个都try…catch…,代码看起来很不整洁,也很难维护,基于这个可以用这种方式处理:
async function getUserInfo() {
try {
const id = await request.getCurrentId().catch(err => Promise.reject('an error in getCurrentId'))//这里如果有异常,在catch里面做个基本处理,继续抛出
const info = await request.getUserInfo(id).catch(err => Promise.reject(() => {
doSomething()
anotherErrorHandler()
})//在catch里面做个基本处理,继续抛出
} catch(err) {
//统一处理异常
if (typeof err === 'function') err()
else errorHandle(err)
}