迴圈使用非同步等待

在迴圈中使用 async await 時,你可能會遇到其中一些問題。

如果你只是嘗試在 forEach 中使用 await,這將丟擲 Unexpected token 錯誤。

(async() => {
 data = [1, 2, 3, 4, 5];
 data.forEach(e => {
   const i = await somePromiseFn(e);
   console.log(i);
 });
})();

這是因為你錯誤地將箭頭函式視為一個塊。await 將在回撥函式的上下文中,而不是 async
直譯器保護我們不會出現上述錯誤,但是如果你將 async 新增到 forEach 回撥中,則不會丟擲任何錯誤。你可能認為這解決了問題,但它無法按預期工作。

例:

(async() => {
  data = [1, 2, 3, 4, 5];
  data.forEach(async(e) => {
    const i = await somePromiseFn(e);
    console.log(i);
  });
  console.log('this will print first');
})();

發生這種情況是因為回撥非同步函式只能暫停,而不是父非同步函式。

你可以編寫一個 asyncForEach 函式來返回一個 promise,然後你可以像 await asyncForEach(async (e) => await somePromiseFn(e), data ) 那樣基本上你會返回一個在等待和完成所有回撥時解析的 promise。但是有更好的方法可以做到這一點,那就是使用迴圈。

你可以使用 for-of 迴圈或 for/while 迴圈,你選擇哪一個並不重要。

(async() => {
  data = [1, 2, 3, 4, 5];
  for (let e of data) {
    const i = await somePromiseFn(e);
    console.log(i);
  }
  console.log('this will print last');
})();

但還有另一個問題。此解決方案將等待每次呼叫 somePromiseFn 完成,然後再迭代下一個。
如果你真的希望按順序執行 somePromiseFn 呼叫,但是如果你希望它們同時執行,則需要在 Promise.all 上進行此操作。

(async() => {
 data = [1, 2, 3, 4, 5];
 const p = await Promise.all(data.map(async(e) => await somePromiseFn(e)));
 console.log(...p);
})();

Promise.all 接收一個 promises 陣列作為其唯一引數並返回一個 promise。當解析陣列中的所有 promise 時,也會解析返回的 promise。我們知道這個承諾,當它解決了我們所有的值。

以上示例是完全可執行的。somePromiseFn 函式可以作為具有超時的非同步回顯函式。你可以嘗試使用至少 stage-3 預設的 babel-repl 中的示例並檢視輸出。

function somePromiseFn(n) {
 return new Promise((res, rej) => {
   setTimeout(() => res(n), 250);
 });
}