摘要:蛤當你嘗試在循環中使用時,事情就會變得復雜一些。這意味著循環中的應該按順序執行。在循環中使用首先,使用對數組進行遍歷。在中使用如果在中使用始終返回數組,這是因為異步函數總是返回。在循環中使用當你使用時,希望篩選具有特定結果的數組。
async 與 await 的使用方式相對簡單。 蛤當你嘗試在循環中使用await時,事情就會變得復雜一些。
想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等著你!
在本文中,分享一些在如果循環中使用await值得注意的問題。
準備一個例子對于這篇文章,假設你想從水果籃中獲取水果的數量。
const fruitBasket = { apple: 27, grape: 0, pear: 14 };
你想從fruitBasket獲得每個水果的數量。 要獲取水果的數量,可以使用getNumFruit函數。
const getNumFruit = fruit => { return fruitBasket[fruit]; }; const numApples = getNumFruit("apple"); console.log(numApples); //27
現在,假設fruitBasket是從服務器上獲取,這里我們使用 setTimeout 來模擬。
const sleep = ms => { return new Promise(resolve => setTimeout(resolve, ms)) }; const getNumFruie = fruit => { return sleep(1000).then(v => fruitBasket[fruit]); }; getNumFruit("apple").then(num => console.log(num)); // 27
最后,假設你想使用await和getNumFruit來獲取異步函數中每個水果的數量。
const control = async _ => { console.log("Start") const numApples = await getNumFruit("apple"); console.log(numApples); const numGrapes = await getNumFruit("grape"); console.log(numGrapes); const numPears = await getNumFruit("pear"); console.log(numPears); console.log("End") }在 for 循環中使用 await
首先定義一個存放水果的數組:
const fruitsToGet = [“apple”, “grape”, “pear”];
循環遍歷這個數組:
const forLoop = async _ => { console.log("Start"); for (let index = 0; index < fruitsToGet.length; index++) { // 得到每個水果的數量 } console.log("End") }
在for循環中,過上使用getNumFruit來獲取每個水果的數量,并將數量打印到控制臺。
由于getNumFruit返回一個promise,我們使用 await 來等待結果的返回并打印它。
const forLoop = async _ => { console.log("start"); for (let index = 0; index < fruitsToGet.length; index ++) { const fruit = fruitsToGet[index]; const numFruit = await getNumFruit(fruit); console.log(numFruit); } console.log("End") }
當使用await時,希望JavaScript暫停執行,直到等待 promise 返回處理結果。這意味著for循環中的await 應該按順序執行。
結果正如你所預料的那樣。
“Start”; “Apple: 27”; “Grape: 0”; “Pear: 14”; “End”;
這種行為適用于大多數循環(比如while和for-of循環)…
但是它不能處理需要回調的循環,如forEach、map、filter和reduce。在接下來的幾節中,我們將研究await 如何影響forEach、map和filter。
在 forEach 循環中使用 await首先,使用 forEach 對數組進行遍歷。
const forEach = _ => { console.log("start"); fruitsToGet.forEach(fruit => { //... }) console.log("End") }
接下來,我們將嘗試使用getNumFruit獲取水果數量。 (注意回調函數中的async關鍵字。我們需要這個async關鍵字,因為await在回調函數中)。
const forEachLoop = _ => { console.log("Start"); fruitsToGet.forEach(async fruit => { const numFruit = await getNumFruit(fruit); console.log(numFruit) }); console.log("End") }
我期望控制臺打印以下內容:
“Start”; “27”; “0”; “14”; “End”;
但實際結果是不同的。在forEach循環中等待返回結果之前,JavaScrip先執行了 console.log("End")。
實際控制臺打印如下:
‘Start’ ‘End’ ‘27’ ‘0’ ‘14’
JavaScript 中的 forEach不支持 promise 感知,也支持 async 和await,所以不能在 forEach 使用 await 。
在 map 中使用 await如果在map中使用await, map 始終返回promise數組,這是因為異步函數總是返回promise。
const mapLoop = async _ => { console.log("Start") const numFruits = await fruitsToGet.map(async fruit => { const numFruit = await getNumFruit(fruit); return numFruit; }) console.log(numFruits); console.log("End") } “Start”; “[Promise, Promise, Promise]”; “End”;
如果你在 map 中使用 await,map 總是返回promises,你必須等待promises 數組得到處理。 或者通過await Promise.all(arrayOfPromises)來完成此操作。
const mapLoop = async _ => { console.log("Start"); const promises = fruitsToGet.map(async fruit => { const numFruit = await getNumFruit(fruit); return numFruit; }); const numFruits = await Promise.all(promises); console.log(numFruits); console.log("End") }
運行結果如下:
如果你愿意,可以在promise 中處理返回值,解析后的將是返回的值。
const mapLoop = _ => { // ... const promises = fruitsToGet.map(async fruit => { const numFruit = await getNumFruit(fruit); return numFruit + 100 }) // ... } “Start”; “[127, 100, 114]”; “End”;在 filter 循環中使用 await
當你使用filter時,希望篩選具有特定結果的數組。假設過濾數量大于20的數組。
如果你正常使用filter (沒有 await),如下:
const filterLoop = _ => { console.log("Start") const moreThan20 = fruitsToGet.filter(async fruit => { const numFruit = await fruitBasket[fruit] return numFruit > 20 }) console.log(moreThan20) console.log("END") }
運行結果
Start ["apple"] END
filter 中的await不會以相同的方式工作。 事實上,它根本不起作用。
const filterLoop = async _ => { console.log("Start") const moreThan20 = await fruitsToGet.filter(async fruit => { const numFruit = fruitBasket[fruit] return numFruit > 20 }) console.log(moreThan20) console.log("END") } // 打印結果 Start ["apple", "grape", "pear"] END
為什么會發生這種情況?
當在filter 回調中使用await時,回調總是一個promise。由于promise 總是真的,數組中的所有項都通過filter 。在filter 使用 await類以下這段代碼
const filtered = array.filter(true);
在filter使用 await 正確的三個步驟
使用map返回一個promise 數組
使用 await 等待處理結果
使用 filter 對返回的結果進行處理
const filterLoop = async _ => { console.log("Start"); const promises = await fruitsToGet.map(fruit => getNumFruit(fruit)); const numFruits = await Promise.all(promises); const moreThan20 = fruitsToGet.filter((fruit, index) => { const numFruit = numFruits[index]; return numFruit > 20; }) console.log(moreThan20); console.log("End") }在 reduce 循環中使用 await
如果想要計算 fruitBastet中的水果總數。 通常,你可以使用reduce循環遍歷數組并將數字相加。
const reduceLoop = _ => { console.log("Start"); const sum = fruitsToGet.reduce((sum, fruit) => { const numFruit = fruitBasket[fruit]; return sum + numFruit; }, 0) console.log(sum) console.log("End") }
運行結果:
當你在 reduce 中使用await時,結果會變得非常混亂。
const reduceLoop = async _ => { console.log("Start"); const sum = await fruitsToGet.reduce(async (sum, fruit) => { const numFruit = await fruitBasket[fruit]; return sum + numFruit; }, 0) console.log(sum) console.log("End") }
[object Promise]14 是什么 鬼??
剖析這一點很有趣。
在第一次遍歷中,sum為0。numFruit是27(通過getNumFruit(apple)的得到的值),0 + 27 = 27。
在第二次遍歷中,sum是一個promise。 (為什么?因為異步函數總是返回promises!)numFruit是0.promise 無法正常添加到對象,因此JavaScript將其轉換為[object Promise]字符串。 [object Promise] + 0 是object Promise] 0。
在第三次遍歷中,sum 也是一個promise。 numFruit是14. [object Promise] + 14是[object Promise] 14。
解開謎團!
這意味著,你可以在reduce回調中使用await,但是你必須記住先等待累加器!
const reduceLoop = async _ => { console.log("Start"); const sum = await fruitsToGet.reduce(async (promisedSum, fruit) => { const sum = await promisedSum; const numFruit = await fruitBasket[fruit]; return sum + numFruit; }, 0) console.log(sum) console.log("End") }
但是從上圖中看到的那樣,await 操作都需要很長時間。 發生這種情況是因為reduceLoop需要等待每次遍歷完成promisedSum。
有一種方法可以加速reduce循環,如果你在等待promisedSum之前先等待getNumFruits(),那么reduceLoop只需要一秒鐘即可完成:
const reduceLoop = async _ => { console.log("Start"); const sum = await fruitsToGet.reduce(async (promisedSum, fruit) => { const numFruit = await fruitBasket[fruit]; const sum = await promisedSum; return sum + numFruit; }, 0) console.log(sum) console.log("End") }
這是因為reduce可以在等待循環的下一個迭代之前觸發所有三個getNumFruit promise。然而,這個方法有點令人困惑,因為你必須注意等待的順序。
在reduce中使用wait最簡單(也是最有效)的方法是
使用map返回一個promise 數組
使用 await 等待處理結果
使用 reduce 對返回的結果進行處理
const reduceLoop = async _ => {
console.log("Start");
const promises = fruitsToGet.map(getNumFruit);
const numFruits = await Promise.all(promises);
const sum = numFruits.reduce((sum, fruit) => sum + fruit);
console.log(sum)
console.log("End")
}
這個版本易于閱讀和理解,需要一秒鐘來計算水果總數。
從上面看出來什么如果你想連續執行await調用,請使用for循環(或任何沒有回調的循環)。
永遠不要和forEach一起使用await,而是使用for循環(或任何沒有回調的循環)。
不要在 filter 和 reduce 中使用 await,如果需要,先用 map 進一步驟處理,然后在使用 filter 和 reduce 進行處理。
交流干貨系列文章匯總如下,覺得不錯點個Star,歡迎 加群 互相學習。
https://github.com/qq44924588...
我是小智,公眾號「大遷世界」作者,對前端技術保持學習愛好者。我會經常分享自己所學所看的干貨,在進階的路上,共勉!
關注公眾號,后臺回復福利,即可看到福利,你懂的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/104421.html
摘要:事件循環從回調隊列中獲取并將其推入調用堆棧。執行從調用堆棧中移除從調用堆棧中移除快速回顧值得注意的是,指定了事件循環應該如何工作,這意味著在技術上它屬于引擎的職責范圍,不再僅僅扮演宿主環境的角色。 此篇是 JavaScript是如何工作的第四篇,其它三篇可以看這里: JavaScript是如何工作的:引擎,運行時和調用堆棧的概述! JavaScript是如何工作的:深入V8引擎&編寫...
摘要:事件循環從回調隊列中獲取并將其推送到調用堆棧。如何工作請注意,不會自動將您的回調函數放到事件循環隊列中。它設置了一個計時器,當計時器到期時,環境將您的回調函數放入事件循環中,以便將來的某個事件會將其選中并執行它。 我們將通過回顧第一篇文章中單線程編程的缺點,然后在討論如何克服它們來構建令人驚嘆的JavaScript UI。在文章結尾處,我們將分享5個關于如何使用async / awai...
摘要:所以異步編程對語言太重要。異步編程我們就以用戶注冊這個特別常見的場景為例,講講異步編程。這種層層嵌套被稱為回調地獄。相比回調函數而言,代碼可讀性更高,代碼的執行順序一目了然。函數內部語句返回的值,會成為方法回調函數的參數。 單線程是Javascript語言最本質的特性之一,Javascript引擎在運行js代碼的時候,同一個時間只能執行單個任務。 這種模式的好處是實現起來比較簡單,執行...
摘要:函數會在之后的某個時刻觸發事件定時器。事件循環中的這樣一次遍歷被稱為一個。執行完畢并出棧。當定時器過期,宿主環境會把回調函數添加至事件循環隊列中,然后,在未來的某個取出并執行該事件。 原文請查閱這里,略有改動。 本系列持續更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第四章。 現在,我們將會通過回顧單線程環境下編程的弊端及如何克服這些困難以創建令人驚嘆...
閱讀 1260·2021-11-23 09:51
閱讀 1628·2021-11-16 11:45
閱讀 4014·2021-10-09 09:43
閱讀 2682·2021-07-22 16:47
閱讀 944·2019-08-27 10:55
閱讀 3449·2019-08-26 17:40
閱讀 3083·2019-08-26 11:39
閱讀 3228·2019-08-23 18:39