含义
- 内置执行器
- Generator函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行
fn()
。
- Generator函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行
- 更好的语义
- async和await,比起星号和yield,语义更清除了。async表示函数里又异步操作,await表示紧跟在后面的表达式需要等待结果。
- 更广的适用性
- co模块规定,yield命令后面只能时Thunk函数或Promise对象,而async函数的await命令后面,可不是Promise对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即resolved 的Promise对象)
- 返回值时Promise
- async函数的返回值时Promise对象,这比Generator函数的返回值是Iterator对象方便多了。你可以用then方法制定下一步操作。
进一步说,async函数完全可以看作多个异步操作,包装成的一个Promise对象,而await命令就是then方法的语法糖
基本用法
async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,在接着执行函数体后面的语句
async function fn(){
console.log(4)
let f1 = await Promise.resolve(1)
console.log(f1) // 1
let f2 = await Promise.resolve(2)
return f2
}
fn().then(res=>{
console.log(res) // 2
})
console.log(3)
// 4 3 1 2
上面代码中,函数前面的async关键字,表示函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。
function timeout(ms){
return new Promise((resolve)=>{
setTimeout(resolve,ms)
})
}
async function fn(value,ms){
await timeout(ms)
console.log(value)
}
fn('haha',3000)
上面代码会在3秒后输出
haha
async函数有多种使用形式。
// 函数声明
async function fn(){}
// 函数表达式
let fn1 = async function(){}
// 对象的方法
let obj = {async foo(){}}
obj.foo().then(()=>console.log(111)) // 111
// class 的方法
class fn2{
constructor(name){
this.name = name;
}
async getName(){
return this.name
}
}
let name = new fn2('小明');
name.getName().then(res=>console.log(res)) // 小明
语法
async函数返回一个Pormise对象。
async函数内部return语句返回的值,会成为then
方法回调函数的参数
let fn = async function(){
return 111
}
fn().then(res=>{console.log(res)}) // 111
上面代码,函数
fn
返回的值,会被then
放回回调函数接收到
async内部抛出错误,会导致返回的Promise对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。
let fn = async function(){
throw new Error('出错了')
}
fn().catch(error=>{
console.log(error); // Error: 出错了
})
Promise对象的状态变化
async函数返回的Promise对象,碧玺等到内部所有await命令后面的Promise对象执行完,才会发生状态改变,除非遇到return
语句或者抛出错误。也就是说,只有asunc函数内部的异步操作执行完,才会执行then方法指定的回调函数。
let fn = async function(){
await new Promise((resolve)=>{
setTimeout(resolve,3000)
})
await new Promise((resolve)=>{
setTimeout(resolve,3000)
})
return 1
}
fn().then(res=>{
console.log(res);
})
上面代码,
then
回调函数会在6秒之后输出res
await
正常情况下,await命令后面是一个Promise对象,返回该对象的结果。如果不是Promise对象,就直接返回对应的值
let fn = async function(){
// 等同于
// return 1
return await 1
}
fn().then(res=>{
console.log(res); // 1
})
上面代码中,await命令的参数是数值1,这是等同于
return 1
let fn = async ()=>{
await Promise.reject('出错了')
}
fn().catch(error=>{console.log(error)}) // 出错了
注意:上面代码中,await语句前面没有
return
,但是reject
方法的参数依然传入了catch方法的回调函数。这里如果在await前面加上return,效果是一样的
注意:任何一个await语句后面的Promise对象变为reject,那么整个async函数都会中断执行
有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行
let fn = async ()=>{
try{
await Promise.reject('出错了')
}catch(error){
console.log(error)
}
return 1
}
fn().then(res=>console.log(res)) // 1
另一种写法
let fn = async ()=>{
await Promise.reject('出错了').catch(error=>{
console.log(error)
})
return 1
}
fn().then(res=>console.log(res)) // 1
错误处理
如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject。
let fn = async ()=>{
await Promise.reject('出错了')
return 1
}
fn()
.then(res=>console.log(res))
.catch(error=>{
console.log(error) // 出错了
})
上面代码中,函数fn执行后,await后面的Promise对象抛出了一个错误对象,导致catca方法的回调函数被调用。
防止出错的方法,也是将其放在try...catch
代码块中
let fn = async ()=>{
try{
await Promise.reject('出错了')
}catch(error){
console.log(error);
}
return 1
}
fn()
.then(res=>console.log(res)) // 1
如果有多个await命令,可以统一放在try...catch
中