javascript
boost log 能不能循环覆盖_如何在 JS 循环中正确使用 async 与 await
引言
async / await是ES7的重要特性之一,也是目前社區(qū)里公認(rèn)的優(yōu)秀異步解決方案。目前,async / await這個(gè)特性已經(jīng)是stage 3的建議
然而,由于部分開發(fā)人員對(duì)該語法糖原理的認(rèn)識(shí)不夠清晰,泛濫而不加考慮地隨意使用async/await ,可能會(huì)我們陷入了新的麻煩之中。
概述
下面是隨處可見的現(xiàn)代化前端代碼:
(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
})();
await 語法本身沒有問題,有時(shí)候可能是使用者用錯(cuò)了。當(dāng)?pizzaData?與?drinkData?之間沒有依賴時(shí),順序的 await 會(huì)最多讓執(zhí)行時(shí)間增加一倍的?getPizzaData?函數(shù)時(shí)間,因?yàn)?getPizzaData?與?getDrinkData應(yīng)該并行執(zhí)行。
回到我們吐槽的回調(diào)地獄,雖然代碼比較丑,帶起碼兩行回調(diào)代碼并不會(huì)帶來阻塞。
看來語法的簡(jiǎn)化,帶來了性能問題,而且直接影響到用戶體驗(yàn),是不是值得我們反思一下?
正確的做法應(yīng)該是先同時(shí)執(zhí)行函數(shù),再 await 返回值,這樣可以并行執(zhí)行異步函數(shù):
(async () => {
?const pizzaPromise = selectPizza();
?const drinkPromise = selectDrink();
?await pizzaPromise;
?await drinkPromise;
?orderItems(); // async call
})();
或者使用?Promise.all?可以讓代碼更可讀:
(async () => {
?Promise.all([selectPizza(), selectDrink()]).then(orderItems); // async call
})();
看來不要隨意的 await,它很可能讓你代碼性能降低。
?精讀
仔細(xì)思考為什么 async/await 會(huì)被濫用,筆者認(rèn)為是它的功能比較反直覺導(dǎo)致的。
首先 async/await 真的是語法糖,功能也僅是讓代碼寫的舒服一些。先不看它的語法或者特性,僅從語法糖三個(gè)字,就能看出它一定是局限了某些能力。
舉個(gè)例子,我們利用 html 標(biāo)簽封裝了一個(gè)組件,帶來了便利性的同時(shí),其功能一定是 html 的子集。又比如,某個(gè)輪子哥覺得某個(gè)組件 api 太復(fù)雜,于是基于它封裝了一個(gè)語法糖,我們多半可以認(rèn)為這個(gè)便捷性是犧牲了部分功能換來的。
功能完整度與使用便利度一直是相互博弈的,很多框架思想的不同開源版本,幾乎都是把功能完整度與便利度按照不同比例混合的結(jié)果。
那么回到 async/await 它的解決的問題是回調(diào)地獄帶來的災(zāi)難:
a(() => {
?b(() => {
? ?c();
?});
});
為了減少嵌套結(jié)構(gòu)太多對(duì)大腦造成的沖擊,async/await 決定這么寫:
await a();
await b();
await c();
雖然層級(jí)上一致了,但邏輯上還是嵌套關(guān)系,這不是另一個(gè)程度上增加了大腦負(fù)擔(dān)嗎?而且這個(gè)轉(zhuǎn)換還是隱形的,所以許多時(shí)候,我們傾向于忽略它,所以造成了語法糖的濫用。
理解語法糖
雖然要正確理解 async/await 的真實(shí)效果比較反人類,但為了清爽的代碼結(jié)構(gòu),以及防止寫出低性能的代碼,還是挺有必要認(rèn)真理解 async/await 帶來的改變。
首先 async/await 只能實(shí)現(xiàn)一部分回調(diào)支持的功能,也就是僅能方便應(yīng)對(duì)層層嵌套的場(chǎng)景。其他場(chǎng)景,就要?jiǎng)右恍┠X子了。
比如兩對(duì)回調(diào):
a(() => {
?b();
});
c(() => {
?d();
});
如果寫成下面的方式,雖然一定能保證功能一致,但變成了最低效的執(zhí)行方式:
await a();
await b();
await c();
await d();
因?yàn)榉g成回調(diào),就變成了:
a(() => {
?b(() => {
? ?c(() => {
? ? ?d();
? ?});
?});
});
然而我們發(fā)現(xiàn),原始代碼中,函數(shù)?c?可以與?a同時(shí)執(zhí)行,但 async/await 語法會(huì)讓我們傾向于在?b?執(zhí)行完后,再執(zhí)行?c。
所以當(dāng)我們意識(shí)到這一點(diǎn),可以優(yōu)化一下性能:
const resA = a();
const resC = c();
await resA;
b();
await resC;
d();
但其實(shí)這個(gè)邏輯也無法達(dá)到回調(diào)的效果,雖然?a?與?c?同時(shí)執(zhí)行了,但?d?原本只要等待?c?執(zhí)行完,現(xiàn)在如果?a?執(zhí)行時(shí)間比?c?長(zhǎng),就變成了:
a(() => {
?d();
});
看來只有完全隔離成兩個(gè)函數(shù):
(async () => {
?await a();
?b();
})();
(async () => {
?await c();
?d();
})();
或者利用?Promise.all:
async function ab() {
?await a();
?b();
}
async function cd() {
?await c();
?d();
}
Promise.all([ab(), cd()]);
這就是我想表達(dá)的可怕之處。回調(diào)方式這么簡(jiǎn)單的過程式代碼,換成 async/await 居然寫完還要反思一下,再反推著去優(yōu)化性能,這簡(jiǎn)直比回調(diào)地獄還要可怕。
而且大部分場(chǎng)景代碼是非常復(fù)雜的,同步與 await 混雜在一起,想捋清楚其中的脈絡(luò),并正確優(yōu)化性能往往是很困難的。但是我們?yōu)槭裁匆约和诳釉偬羁幽?#xff1f;很多時(shí)候還會(huì)導(dǎo)致忘了填。
原文作者給出了?Promise.all?的方式簡(jiǎn)化邏輯,但筆者認(rèn)為,不要一昧追求 async/await 語法,在必要情況下適當(dāng)使用回調(diào),是可以增加代碼可讀性的。
總結(jié)
async/await 回調(diào)地獄提醒著我們,不要過渡依賴新特性,否則可能帶來的代碼執(zhí)行效率的下降,進(jìn)而影響到用戶體驗(yàn)。同時(shí),筆者認(rèn)為,也不要過渡利用新特性修復(fù)新特性帶來的問題,這樣反而導(dǎo)致代碼可讀性下降。
*聲明:本文于網(wǎng)絡(luò)整理,版權(quán)歸原作者所有,如來源信息有誤或侵犯權(quán)益,請(qǐng)聯(lián)系我們刪除或授權(quán)事宜。
公眾號(hào)ID:tzbc666掃碼關(guān)注最新動(dòng)態(tài)點(diǎn)個(gè)好看和轉(zhuǎn)發(fā)也是一種支持喲!總結(jié)
以上是生活随笔為你收集整理的boost log 能不能循环覆盖_如何在 JS 循环中正确使用 async 与 await的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么恢复初始状态_汽车多少公里应该清洗节
- 下一篇: python openoffice_wi