forEach與async/await使用踩坑
阿新 • • 發佈:2020-09-15
function test() { let arr = [1, 2, 3] arr.forEach(async item => { const res = await fetch(item) console.log(res) }) console.log('end') } function fetch(x) { return new Promise((resolve, reject) => { resolve(x) }) } test()
思考如上程式碼、我們預期結果是先輸出1,2,3,然後因為等待非同步結果最後輸出end
但是實際上輸出卻是end先輸出,才到1,2,3。
原因如下:
1、首先這是因為foreach是沒有return返回值的(可以自己去跟下原始碼,foreach內部實現只是簡單的回撥)
2、而foreach裡面的回撥函式因為加了async的原因,所以預設會返回一個promise,但是因為foreach的實現並沒有返回值,所以導致返回的這個promise物件沒人去管了
首先為了保證end最後輸出,我們肯定要先等待迴圈的返回結果因此改成如下程式碼
async function test() { let arr = [1, 2, 3] await arr.forEach(async item => { const res = await fetch(item) console.log(res) }) console.log('end') }
但是這樣改之後依然行不通,原因是foreach沒有返回值,所以我們必須保證迴圈能夠有返回值,所以要將foreach改成map
async function test() { let arr = [1, 2, 3] await arr.map(async item => { const res = await fetch(item) console.log(res) }) console.log('end') }
結果依然不行,然後我們會發現其實map返回的並不是一個promise物件,而是一個包含promise物件的陣列[promise, promise, promise],其中每個promise物件都是迴圈迭代產生的結果。而await是處理不了陣列的,它只能處理promise物件。考慮到這一點我們基本上就差不多知道如何改正了、有兩種方法。
第一是將迴圈改成常規的遍歷方式
async function test() { let arr = [1, 2, 3] for(let i in arr){ const res = await fetch(arr[i]) console.log(res) } console.log('end') }
第二種就比較高端了,使用Promise.all(),這是一個專門處理promise陣列的方法,當async標記的箭頭函式返回一個promise物件時,map方法得到的就是一個promise物件陣列,然後我們將這個陣列丟給Promise.all()去依次執行,然後只需要使用await去等待執行結果,就能保證後面的end在得到結果後才會被輸出,得到最終輸出結果1,2,3,end
async function test() { let arr = [1, 2, 3] await Promise.all(arr.map(async item => { const res = await fetch(item) console.log(res) })) console.log('end') }