含義
- 內置執行器
- 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
中