爲何,async這顆糖很甜,也很鹹呢?
你們都知道,async函數是Generator函數的語法糖,那什麼是Generator函數呢? 若是你還沒了解Generator,Generator異步調用,這篇文章爲你介紹了Generator函數的原理和使用方法。
咱們先來看一個最簡單的Generator函數javascript
function * generator(x){
var y = yield x + 1;
return y;
}
var gen = generator();
gen.next(1); //{value:2,done:false}
複製代碼
這個案例是最簡單的Generator函數的實現,咱們再來看一下,async函數的簡單例子java
async function generator(x){
var y = await x + 1;
return y;
}
genertor(1).then(value => console.log(value)); //2
複製代碼
對比上面兩段代碼,明顯感受到,使用async後,代碼變得更加清晰,同時,再也不須要手動調用next方法,就行實現異步加載,固然,爲了代碼清晰,上面的代碼並沒用異步調用,正常狀況下,await後面的表達式爲promise對象。
相對於Generator函數,async有下面幾點好處 :git
先定義一個 Fetch 方法用於獲取 github user 的信息:github
function fetchUser() {
return new Promise((resolve, reject) => {
fetch('https://api.github.com/users/superman66')
.then((data) => {
resolve(data.json());
}, (error) => {
reject(error);
})
});
}
複製代碼
Promise 方式json
function getUserByPromise() {
fetchUser()
.then((data) => {
console.log(data);
}, (error) => {
console.log(error);
})
}
getUserByPromise();
複製代碼
使用promise後,代碼的執行變的很清晰,這能解決咱們當前問題,但有一種狀況,若是咱們的需求有屢次請求,切每次請求都須要上次請求的結果,這就變的很麻煩了。固然,有的小夥伴說了,那咱們就能夠繼續的then()下去啊,能夠,絕對能夠,可是,隨着請求次數的增多,代碼中出現更多數量的then,若是不加以註釋,也會變的晦澀難懂,這不是咱們想要的結果。繼續。。。 Generator 方式api
function* fetchUserByGenerator() {
const user = yield fetchUser();
return user;
}
const g = fetchUserByGenerator();
const result = g.next().value;
result.then((v) => {
console.log(v);
}, (error) => {
console.log(error);
})
複製代碼
Generator 的方式解決了 Promise 的一些問題,流程更加直觀、語義化。可是 Generator 的問題在於,函數的執行須要依靠執行器,每次都須要經過 g.next() 的方式去執行。
async 方式promise
async function getUserByAsync(){
let user = await fetchUser();
return user;
}
getUserByAsync()
.then(v => console.log(v));
複製代碼
哇。。。一樣的結果,async像絲般順滑,搞定了。真甜。。。異步
前文說到,async函數返回一個Promise對象。async
async function generator(x){
var y = await x + 1;
return y;
}
genertor(1).then(value => console.log(value)); //2
複製代碼
promise函數then方法的執行前提是,await後面的表達式以獲取到值。而後將值以參數的形式傳入後面的函數中(例子中直接在控制檯中打印出來)。 await後面能夠是promise對象,也能夠是基礎數據。async 函數返回的 Promise 對象,必須等到內部全部的 await 命令的 Promise 對象執行完,纔會發生狀態改變。函數
const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout));
async function f(){
await delay(1000);
await delay(2000);
await delay(3000);
return 'done';
}
f().then(v => console.log(v)); // 等待6s後才輸出 'done'
複製代碼
正常狀況下,await 命令後面跟着的是 Promise ,若是不是的話,也會被轉換成一個 當即 resolve 的 Promise。
async function f() {
return await 1
};
f().then( (v) => console.log(v)) // 1
複製代碼
若是await後面的異步操做出錯,那麼等同於async函數返回的 Promise 對象被reject。
async function f() {
await new Promise(function (resolve, reject) {
throw new Error('出錯了');
});
}
f()
.then(v => console.log(v))
.catch(e => console.log(e))
// Error:出錯了
複製代碼
上面代碼中,async函數f執行後,await後面的 Promise 對象會拋出一個錯誤對象,致使catch方法的回調函數被調用,它的參數就是拋出的錯誤對象。 防止出錯的方法,也是將其放在try...catch代碼塊之中。
async function f() {
try {
await new Promise(function (resolve, reject) {
throw new Error('出錯了');
});
} catch(e) {
}
return await('hello world');
}
複製代碼
若是有多個await命令,能夠統一放在try...catch結構中。
async function main() {
try {
const val1 = await firstStep();
const val2 = await secondStep(val1);
const val3 = await thirdStep(val1, val2);
console.log('Final: ', val3);
}
catch (err) {
console.error(err);
}
}
複製代碼
當咱們在編寫JavaScript異步代碼的時候,人們常常在一個接着一個的函數調用前面添加await關鍵字.這會致使性能問題,由於在一般狀況下,一個語句的執行並不依賴前一個語句的執行,可是由於添加了await關鍵字,你仍舊須要等待前一個語句執行完才能執行一個語句.
(async () => {
const pizzaData = await getPizzaData() // async call
const drinkData = await getDrinkData() // async call
const chosenPizza = choosePizza() // sync call
const chosenDrink = chooseDrink() // sync call
await addPizzaToCart(chosenPizza) // async call
await addDrinkToCart(chosenDrink) // async call
orderItems() // async call
})()
複製代碼
解釋: 1.得到披薩的列表. 2.得到飲料的列表. 3.從披薩列表中選擇披薩. 4.從飲料列表中選擇飲料. 5.把選擇的披薩加入購物車 6.把選擇的飲料加入購物車. 7.確認訂單 錯誤:得到披薩列表和飲料列表能夠同時進行,不必等待獲取披薩列表後再去獲取飲料列表。 如何解決這個問題呢?
async function selectPizza() {
const pizzaData = await getPizzaData() // async call
const chosenPizza = choosePizza() // sync call
await addPizzaToCart(chosenPizza) // async call
}
async function selectDrink() {
const drinkData = await getDrinkData() // async call
const chosenDrink = chooseDrink() // sync call
await addDrinkToCart(chosenDrink) // async call
}
(async () => {
const pizzaPromise = selectPizza()
const drinkPromise = selectDrink()
await pizzaPromise
await drinkPromise
orderItems() // async call
})()
// 我更喜歡下面這種實現.
(async () => {
Promise.all([selectPizza(), selectDrink()]).then(orderItems) // async call
})()
複製代碼
很清晰的展示出,代碼的執行順序,相較於上一個,性能有了明顯的提升。 ##總結 仍是題目的那句話,async函數很甜,也很鹹。不要過度的去使用async/await,由於徹底不影響異步執行的操做,若是非要同步執行,隨着調用接口的增長,回嚴重影響性能。瑕不掩瑜,async的便利性,很大程度上會減小開發量,同時代碼的可讀性也有很大的提升。
若是你以爲這篇文章對你有幫助,別忘了給點個贊呦~ 每週會給你們分享一到兩篇文章,但願跟你們一塊兒學習,一塊兒進步。