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') }