入冬了,回想起 2016 年的那個寒冬,我遭遇了裁人的風波。給我敲響了警鐘,基礎知識不能忘,時常要複習複習,才能應對一些突如其來的事情,今天篇文章也是由於以前被考到,記憶模糊回答的不是很肯定,因此提筆再次記錄一番。javascript
ES7 引入了 async、await 方法,來處理異步操做。關於這倆方法的使用,不贅述,網上的文章多如牛毛,又寫一篇入門,未免廢話連篇。今天要分享的內容是在js的一些循環方法中,async 和 await 的表現是如何的。java
在 for 循環中,async 和 await 表現的仍是使人滿意。如今假設一個場景,設置一個變量,爸媽我三我的的體重,求和數組
const family = { "dad": 150, "mom": 100, "son": 200 } const familyToGet = ["dad", "mom", "son"] // 寫一個sleep方法 const sleep = ms => { return new Promise(resolve => setTimeout(resolve, ms)) } // 假設獲取體重數據的過程是一個請求接口的異步過程 const getFamilyWeight = person => { return sleep(2000).then(() => family[person]) } const loop_for = async () => { console.log('start') let result = 0 for(let i = 0; i < familyToGet.length; i++) { const person = familyToGet[i] const weight = await getFamilyWeight(person) result += weight } console.log('result:', result) console.log('end') } loop_for() // 運行結果 // start // 150 // 100 // 200 // result: 450 // end 複製代碼
按照上述代碼的打印結果分析,for 循環內的代碼,每一次都會等當前的 getFamilyWeight 方法返回了內容以後,纔會繼續下一個循環,循環所有結束的時候,纔會跑 for 循環下面的代碼,這是咱們想要的結果,nice。promise
在 forEach 中,async 和 await 表現的差強人意,具體緣由,還應歸結於 forEach 中的回調函數,具體看看代碼的表現。markdown
const family = { "dad": 150, "mom": 100, "son": 200 } const familyToGet = ["dad", "mom", "son"] // 寫一個sleep方法 const sleep = ms => { return new Promise(resolve => setTimeout(resolve, ms)) } // 假設獲取體重數據的過程是一個請求接口的異步過程 const getFamilyWeight = person => { return sleep(2000).then(() => family[person]) } const loop_forEach = () => { console.log('start') let result = 0 // 在回調函數中,異步是很差控制的 familyToGet.forEach(async person => { const num = await getFamilyWeight(person) console.log(num) result += num }) console.log('result', result) console.log('end') } loop_forEach() // 運行結果 // start // result 0 // end // 150 // 100 // 200 複製代碼
從運行結果中,能夠看出,代碼沒有等待forEach方法執行結束,而是直接繼續日後執行了。回調函數傳進去以後,2秒鐘以後纔會返回數據,這不是咱們想要的結果,我如今是但願它能順序的執行。異步
那麼咱們分析一下 forEach 內部大體的原理async
Array.prototype.forEach = function(cb) { // this 爲當前調用該函數的變量 for(let i=0; i < this.length; i++) { cb(this[i], i); } } 複製代碼
且看上面的簡易 forEach 重寫,其實內部也是一個 for 循環,那麼爲何不能表現的和 for 循環同樣的運行結果呢?函數
咱們的回調函數進入 forEach 以後,又被單獨的以 cb() 這種形式執行了一次,至關於在內部又建立了一個 async 、await 形式的方法,當 forEach 函數執行完的時候,至關於建立了 3 個方法,這 3 個方法須要等待2 秒鐘,數據纔會返回,而 forEach 外面的代碼是不會等這 3 個函數返回內容再執行,而是獨斷獨行的管本身走了,因此纔會形成這樣的局面。返回問題的根本,我如今的需求是想要代碼能順序執行,且最後能算出一家人體重之和,下面咱們來修改一下代碼。oop
const family = { "dad": 150, "mom": 100, "son": 200 } const familyToGet = ["dad", "mom", "son"] // 寫一個sleep方法 const sleep = ms => { return new Promise(resolve => setTimeout(resolve, ms)) } // 假設獲取體重數據的過程是一個請求接口的異步過程 const getFamilyWeight = person => { return sleep(2000).then(() => family[person]) } const loop_forEach = async () => { console.log('start') let promise = [] // 在回調函數中,異步是很差控制的 familyToGet.forEach(person => { const num = getFamilyWeight(person) promise.push(num) }) const result = await Promise.all(promise) console.log('result', result) const weight = result.reduce((sum, personWeight) => sum + personWeight) console.log('weight', weight) console.log('end') } loop_forEach() // 運行結果 // start // result [150, 100, 200] // weight 450 // end 複製代碼
在 forEach 的回調函數內,直接把 getFamilyWeight 方法返回的 promise 對象 push 到 promise 這個數組變量內,經過 Promise.all 來作一層等待的異步處理。this
在 map 中,直接返回一個 promise 數組
const family = { "dad": 150, "mom": 100, "son": 200 } const familyToGet = ["dad", "mom", "son"] // 寫一個sleep方法 const sleep = ms => { return new Promise(resolve => setTimeout(resolve, ms)) } // 假設獲取體重數據的過程是一個請求接口的異步過程 const getFamilyWeight = person => { return sleep(2000).then(() => family[person]) } const loop_map = async () => { console.log('start') // map中返回的是一個promise數組,須要經過Promise.all處理 const promise = familyToGet.map(async person => { const num = await getFamilyWeight(person) return num; }) console.log('promise', promise) // 等待promise裏的內容所有返回,纔會繼續往下執行 const result = await Promise.all(promise) console.log('result', result) const weight = result.reduce((sum, personWeight) => sum + personWeight) console.log('weight', weight) console.log('end') } loop_map() // 運行結果 // start // promise [Promise, Promise, Promise] // result [150, 100, 200] // weight 450 // end 複製代碼
你們應該能感受到其實這個和上面改版後的 forEach 差很少,沒錯,正如你所料,只不過 map 方法能返回新的數組罷了,forEach 則不能返回數組。
在這裏把 map 的簡單實現寫一下,方便理解
Array.prototype.map = function(cb) { // this 爲當前調用該函數的變量 var o = []; for(let i=0; i < this.length; i++) { var temp = cb(this[i], i); o.push(temp); } return o; } 複製代碼
如上述代碼,在外面經過 o 這個變量收集 cb() 回調函數的返回值,再到外面統一處理。真是妙啊,你們能夠細細品味一番,能吾出不少新的對象。
一、for循環內使用async和await仍是能夠的,穩如老狗🐶
二、不要在forEach方法內使用async、await,儘可能避免這種寫法,坑啊。。。