Async/Await in Array Loops
隨著 ES6
的 Promise
解決 callbacks hell 的問題後,取得代之的就是 Es7
的 Async/Await
,都是為了達成 AJAX 獲得更好的體驗。這篇簡單紀錄一下使用 Async/Await
在迴圈裡面遇到的問題,以及該如何解決,以下就以三個例子來舉例:
ForEach
如果是 ForEach
搭配 Async/Await
的時候,會發生什麼事情呢?我們直接來深入了解吧!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const urls = [ 'https://jsonplaceholder.typicode.com/todos/1', 'https://jsonplaceholder.typicode.com/todos/2', 'https://jsonplaceholder.typicode.com/todos/3' ];
async function getTodos() { await urls.forEach(async (url, idx) => { console.log(`Received Todo ${idx+1}:`, await fetch(url)); }); console.log('Finished!'); }
getTodos();
|
雖然程式碼都沒有任何錯誤,也不會噴錯跑不成功,但是相信眼尖的你一定會注意 Finished!
居然在 await urls.forEach
之前就執行了!不是說好的 async
要 await
嗎?誰跟你說好~如以下的結果所示:
1 2 3 4 5 6
|
Finished! Received Todo 1, Response: { type: "cors", url: "https://jsonplaceholder.typicode.com/todos/1", redirected: false, status: 200, ok: true, … } Received Todo 3, Response: { type: "cors", url: "https://jsonplaceholder.typicode.com/todos/3", redirected: false, status: 200, ok: true, … } Received Todo 2, Response: { type: "cors", url: "https://jsonplaceholder.typicode.com/todos/2", redirected: false, status: 200, ok: true, … }
|
那不能用 ForEach
我們該如何解決呢?來看看底下的兩種方式吧!
Map + Promise.all
Promise.all() 方法回傳一個 Promise 物件,當引數 iterable 中所有的 promises 都被實現(resolved),或引數 iterable 不含任何 promise 時,被實現。或以第一個被拒絕的 promise 的原因被拒絕。
- 一個已被實現(already resolved)的
Promise
,若傳入的 iterable 為空。
- 一個非同步地被實現(asynchronously resolved)的 Promise 若傳入的 iterable 不含 promise。注意,Google Chrome 58 對此情形回傳一個已被解決的 promise。
- 一個擱置(pending)的 Promise,對所有剩餘情形。此 promise 接著被非同步地被 resolved/rejected(只要堆疊為空)當 iterable 中所有的 promises 都被實現,或其中一個被拒絕。參見下方關於”Promise.all 的非同步與同步性質”的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const urls = [ 'https://jsonplaceholder.typicode.com/todos/1', 'https://jsonplaceholder.typicode.com/todos/2', 'https://jsonplaceholder.typicode.com/todos/3' ];
async function getTodos() { const promises = urls.map(async (url, idx) => console.log(`Received Todo ${idx+1}:`, await fetch(url)) );
await Promise.all(promises);
console.log('Finished!'); }
getTodos();
|
For…of
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const urls = [ 'https://jsonplaceholder.typicode.com/todos/1', 'https://jsonplaceholder.typicode.com/todos/2', 'https://jsonplaceholder.typicode.com/todos/3' ];
async function getTodos() { for (const [idx, url] of urls.entries()) { console.log(`Received Todo ${idx+1}:`, await fetch(url)); }
console.log('Finished!'); }
getTodos();
|
上面兩種方法都可以得到:
1 2 3 4
| Received Todo 1, Response: { type: "cors", url: "https://jsonplaceholder.typicode.com/todos/1", redirected: false, status: 200, ok: true, … } Received Todo 2, Response: { type: "cors", url: "https://jsonplaceholder.typicode.com/todos/2", redirected: false, status: 200, ok: true, … } Received Todo 3, Response: { type: "cors", url: "https://jsonplaceholder.typicode.com/todos/3", redirected: false, status: 200, ok: true, … } Finished!
|
參考資料