[譯]async-await 數組循環的幾個坑

[譯]async-await 數組循環的幾個坑

在 Javascript 循環中使用 async/ await 循環遍歷數組彷佛很簡單,可是在將二者結合使用時須要注意一些非直觀的行爲。讓咱們看看三個不一樣的例子,看看你應該注意什麼,以及哪一個循環最適合特定用例。json

forEach 循環的狀況

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) => { 
    const todo = await fetch(url);
    console.log(`Received Todo ${idx+1}:`, todo);
  });
  
  console.log('Finished!');
}

getTodos();

20190309102916.png

Finished!
Received Todo 2, Response: { ··· }
Received Todo 1, Response: { ··· }
Received Todo 3, Response: { ··· }

⚠️問題 1:數組

如上述代碼可以正常執行。可是注意 Finished! 消息,被率先打印了。儘管咱們使用了 await 但他仍然不會等待全部 await 執行完畢promise

⚠️ 問題 2:異步

然而,儘管 await 在循環中使用,但它並無等待每一個請求在執行下一個請求以前完成。請求不會按照順序一步一步被髮送出去。若是第一個請求的時間比如下請求的時間長,它仍然能夠在最後完成。async

所以,根據上述緣由,forEach 在和 async/await 搭配使用的時候並非一個靠得住的東西oop

Promise.all 方法

咱們首先須要解決的就是等待全部循環執行完畢。await 操做符返回一個 promise,咱們可使用 Promise.all 方法去並行執行全部的請求。最後去 await 全部 promise 返回的結果性能

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();

20190309102927.png

Received Todo 1, Response: { ··· }
Received Todo 2, Response: { ··· }
Received Todo 3, Response: { ··· }
Finished!

咱們解決了不等待全部請求執行完畢後打印 Finished!,看起來咱們彷佛也解決了請求順序的問題。fetch

實際上,上文中已經提到過,Promise.all 方法會按照並行的模式,將全部請求一次性所有發送出去,而後等待接收到所有結果後,按照順序打印出來而已。它並不會按照順序發送一個請求,收到結果後再發送下一個請求。jsonp

這很是適合不須要按照順序發送的狀況,但若是你想要的是串行發送請求那麼 Promise.all 並不適合url

for-of 循環

以上的兩種方法並不能完美解決那兩個問題。

for-of 循環則可以按照預期順序執行——等待上一個 await 執行完畢後,再接着下一個。

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()) {
    const todo = await fetch(url);
    console.log(`Received Todo ${idx+1}:`, todo);
  }

  console.log('Finished!');
}

getTodos();

20190309102904.png

Received Todo 1, Response: { ··· }
Received Todo 2, Response: { ··· }
Received Todo 3, Response: { ··· }
Finished!

我特別喜歡這種使代碼保持線性的方法,這是使用 async/await 的關鍵優點之一。我以爲它比其餘選擇更容易閱讀。

若是您不須要訪問索引,則代碼變得更加簡潔:

for(ur url of urls){···}

使用for...of循環的一個主要缺點是它與Javascript中的其餘循環選項相比性能不夠好。可是,將性能參數用於await異步調用時,性能參數能夠忽略不計,由於目的是在每一個調用解析以前保持循環。我一般只使用for...of進行異步。

固然你也可使用 for 循環獲得 for-of 循環全部好處。但我仍是喜歡 for-of 循環帶來的簡潔和高可讀性。

相關文章
相關標籤/搜索