async/await是es7推出的一套關於異步的終極解決方案,為什么要說他是終極解決方案呢?因為他實在是太好用了,而且寫起來還非常的簡單。
一:async/await基礎語法
// 定義一個異步函數(假設他是一個異步函數)
getJSON(){
return 'JSON'
}
// 在需要使用上面異步函數的函數前面,加上async聲明,聲明這是一個異步函數
async testAsync() {
// 在異步函數前面加上await,函數執行就會等待用await聲明的異步函數執行完畢之后,在往下執行
await getJSON()
...剩下的代碼
}
以上就是async/await最基本的用法。
還需要注意的一點就是使用async/await的時候,是無法捕獲錯誤的,這個時候就要用到我們es5里面一個被大家遺忘了的try/catch,來進行錯誤的捕獲:
async testAsync() {
try {
await getJSON()
} catch(err) {
console.log(err)
}
...剩下的代碼
}
注意:
1.async函數在聲明形式上和普通函數沒有區別,函數聲明式,函數表達式,對象方法,class方法和箭頭函數等都可以聲明async函數。
2.任何一個await語句后面的 Promise 對象變為reject狀態,那么整個async函數都會中斷執行。
3.async函數返回的 Promise 對象,必須等到內部所有await命令后面的 Promise 對象執行完,才會發生狀態改變,除非遇到return語句或者拋出錯誤。也就是說,只有async函數內部的異步操作執行完,才會執行then方法指定的回調函數。
二:async/await
async
async這個單詞大家應該都比較熟悉,他是英文單詞‘異步’的簡寫,代表的意思也是異步。
async function testAsync() {
return "hello async";
}
const result = testAsync();
console.log(result);
輸出結果:
Promise{<resolved>: "hello async"}
可以看出async函數,返回的是一個Promise對象
await
await是英文單詞‘等待’的意思,代表的意思也是等待,那他等的到底是個什么東西呢?還是一個Promise。
三 async/await和Generator
Generator函數:generator(生成器)是ES6標准引入的新的數據類型。一個generator看上去像一個函數,但可以返回多次。下面面是一個Generator函數的簡單寫法。
function* Generator() {
yield '11111111';
yield '22222222'
return '3333333';
}
let aaa = Generator();
Generator函數和普通函數一樣通過函數名+()去調用,但是調用完之后並不執行。它僅僅是創建了一個generator對象,還沒有去執行它。想要運行Generator函數,需要通過遍歷器對象的next方法。
let a = aaa.next()
let b = aaa.next()
let c = aaa.next()
let d = aaa.next()
console.log(a,b,c,d) // {value: "11111111", done: false} {value: "22222222", done: false} {value: "3333333", done: true} {value: undefined, done: true}
想要Generator函數執行下一步,必須調用遍歷器對象的next方法,使得指針移向下一個狀態。也就是說,每次調用next方法,內部指針就從函數頭部或上一次停下來的地方開始執行,直到遇到下一個yield表達式或return語句。由此可見,Generator 函數是分段執行的,yield表達式是暫停執行的標記,而next方法可以恢復執行。也就是上面說的可以交出函數的執行權。
上面對Generator函數做了一個簡單的介紹,接下來說一下async/await和Generator。
根據阮一峰老師的介紹,async函數就是Generator函數的語法糖。
圖片:
代碼上看起來,async函數就是將 Generator 函數的星號(*)替換成async,將yield替換成await。
實際上async函數對Generator函數的改進,體現在一下四點:
1.async函數自帶執行器,所以執行方式和普通函數的執行方式一樣,通過函數名+()的方式執行。
2.async和await比起*和yield在語義上更清楚。
3.co模塊約定,yield命令后面只能是 Thunk 函數或 Promise 對象,而async函數的await命令后面,可以是 Promise 對象和原始類型的值(數值、字符串和布爾值,但這時會自動轉成立即 resolved 的 Promise 對象)。
4.async函數的返回值是 Promise 對象,這比 Generator 函數的返回值是 Iterator 對象方便多了。你可以用then方法指定下一步的操作。
進一步說,async函數完全可以看作多個異步操作,包裝成的一個 Promise 對象,而await命令就是內部then命令的語法糖。
四:async/await和Promise
上面說了async/await和Generator的關系,這里再說一下和Promise的關系,async/await其實是基於Promise的。async函數其實是把Promise包裝了一下。
下面是一個async/await的寫法:
getConstant() {
return 1
}
async getAsyncConstant() {
return 1
}
async getPromise() {
return new Promise((resolved, rejected)=> {
resolved(1)
});
}
async test() {
let a = 2
let c = 1
await getConstant();
let d = 3
await getPromise();
let d = 4
await getAsyncConstant();
return 2
}
上面的代碼其實真正的在解析執行的時候是這樣的:
function getConstant() {
return 1;
}
function getAsyncConstant() {
return Promise.resolve().then(function () {
return 1;
});
}
function getPromise() {
return Promise.resolve().then(function () {
return new Promise((resolved, rejected) => {
resolved(1);
});
});
}
test() {
return Promise.resolve().then(function () {
let a = 2;
let c = 1;
return getConstant();
}).then(function () {
let d = 3;
return getPromise();
}).then(function () {
let d = 4;
return getAsyncConstant();
}).then(function () {
return 2;
});
}
通過上面的代碼可以看出async/await的本身還是基於Promise的。
因為await本身返回的也是一個Promise,它只是把await后面的代碼放到了await返回的Promise的.then后面,以此來實現的。
做個練習:
function getJson(){
return new Promise((reslove,reject) => {
setTimeout(function(){
console.log(2)
reslove(2)
},2000)
})
}
async function testAsync() {
await getJson()
console.log(3)
}
testAsync()
上面的代碼是會先打印2還是3?
答案是2,3
看過上面的童鞋應該知道其實他的真實代碼是這樣的:
function getJson(){
return new Promise((reslove,reject) => {
setTimeout(function(){
console.log(2)
reslove()
},2000)
})
}
function testAsync() {
return new Promise((reslove,reject) => {
getJson().then(function (res) {
console.log(3)
})
})
}
testAsync()