一、基本介紹
1、語法定義
for await...of
語句創建一個循環,該循環遍歷異步可迭代對象以及同步可迭代對象,包括: 內置的 String
, Array
,類似數組對象 (例如 arguments
或 NodeList
),TypedArray
, Map
, Set
和用戶定義的異步/同步迭代器。它使用對象的每個不同屬性的值調用要執行的語句來調用自定義迭代鈎子。
類似於 await
運算符一樣,該語句只能在一個async function 內部使用。
2、如何使用
function getTime(seconds){ return new Promise(resolve=>{ setTimeout(() => {
console.log(seconds) resolve(seconds) }, seconds); }) } function test(){ let arr = [getTime(2000), getTime(300), getTime(1000)] for (let x of arr){ console.log(x); } } test()
對比區別,這里沒有 await 。你可以猜測下這個會怎么輸出?分別是 2000的Promise、300的Promise、1000的Promise 順序輸出的,然后再按時間延時打印 300、1000、2000
那么我們再想一下,如果我就是需要按 2000、300、1000 的順序輸出怎么辦呢?
首先想到的是 async await 實現是可以的,但是會有缺陷。如果有好幾個promise或者異步任務,就會寫相應數量的 await,代碼量變得龐大臃腫。所以使用 for await of 來實現
此時的輸出就是 Promise undefined,然后過 2s 后,按 2000、300、1000 的順序輸出。
3、執行機制:for await of 循環可以暫停循環,當第一個異步執行完成后才會執行下一個,最后結果是讓輸出結果保持同步順序輸出。
什么意思呢?我們自己直接試試就知道了。
(1)上面代碼 arr = [getTime(200), getTime(3000), getTime(1000)] 時,先打印 200,然后過 2s,再一起打印3000、1000
(2)[getTime(1000), getTime(5000), getTime(6000)] 時,先打印 1000,然后過4s,打印 5000,再過1s打印6000
二、async+await 遇見 forEach和 for···of
1、首先看 2 道題,自己先考慮下,能不能答對。
// 定義一個fetch函數模擬異步請求
function fetch(x) { return new Promise((resolve, reject) => { console.log('aaa'); setTimeout(() => { resolve(x) }, 500 * x) }) } // 第一題:
function test() { let arr = [3, 2, 1] arr.forEach(async item => { const res = await fetch(item) console.log(res) }) console.log('end') } test(); // 輸出什么 // 第二題:
async function test() { let arr = [3, 2, 1] for (const item of arr) { const res = await fetch(item) console.log(res) } console.log('end') } test(); // 輸出什么
第一題:先同時先3個aaa和end,test函數執行完成返回undefined,然后異步回調每隔500ms依次打印1、2、3
第二題:先aaa,1500ms后打印 3,再 aaa,1s后打印2,再aaa,500ms后打印1,再end,再test函數返回值undefined。
這里我理解錯了,test函數執行完成,就應該返回 Promise undefined,也就是第二題輸出是這樣的:
這里需要特別注意下 end 的打印,這里涉及到“協程”的概念理解,asyc await 本質是利用協程來實現的。
2、為什么同樣是遍歷,輸出結果卻不一樣呢?
因為 for...of 內部處理的機制和 forEach 不同,forEach 是直接調用回調函數,for...of 是通過迭代器的方式去遍歷。
3、兩者的處理機制:
// 參考下 Polyfill 版本的 forEach,簡化后的偽代碼:
while (index < arr.length) { callback(item, index) //我們傳入的回調函數
}
而使用迭代器寫第二題(既for...of 碼的語法糖)等價於:
async function test() { let arr = [3, 2, 1] const iterator = arr[Symbol.iterator]() //for of會自動調用遍歷器函數
let res = iterator.next() while (!res.done) { const value = res.value const res1 = await fetch(value) console.log(res1) res = iterator.next() } console.log('end') }