首頁 >web前端 >js教程 >JavaScript循環如何使用 async/await?需要注意些什麼?

JavaScript循環如何使用 async/await?需要注意些什麼?

青灯夜游
青灯夜游轉載
2020-11-30 18:15:525027瀏覽

JavaScript循環如何使用 async/await?需要注意些什麼?

asyncawait 的使用方式相對簡單。但當你嘗試在循環中使用await時,事情就會變得更複雜。

在本文中,分享一些在如果循環中使用await值得注意的問題。

準備一個例子

對於這篇文章,假設你想從水果籃中獲得水果的數量。

const fruitBasket = {
 apple: 27,
 grape: 0,
 pear: 14
};

你想從fruitBasket獲得每個水果的數量。要取得水果的數量,可以使用getNumFruit函數。

const getNumFruit = fruit => {
  return fruitBasket[fruit];
};

const numApples = getNumFruit('apple');
console.log(numApples); //27

現在,假設fruitBasket是從伺服器上獲取,這裡我們使用 setTimeout 來模擬。

const sleep = ms => {
  return new Promise(resolve => setTimeout(resolve, ms))
};

const getNumFruie = fruit => {
  return sleep(1000).then(v => fruitBasket[fruit]);
};

getNumFruit("apple").then(num => console.log(num)); // 27

最後,假設你想使用awaitgetNumFruit來取得非同步函數中每個水果的數量。

const control = async _ => {
  console.log('Start')

  const numApples = await getNumFruit('apple');
  console.log(numApples);

  const numGrapes = await getNumFruit('grape');
  console.log(numGrapes);

  const numPears = await getNumFruit('pear');
  console.log(numPears);

  console.log('End')
}

JavaScript循環如何使用 async/await?需要注意些什麼?

在for 循環中使用await

首先定義一個存放水果的陣列:

const fruitsToGet = [“apple”, “grape”, “pear”];

循環遍歷這個陣列:

const forLoop = async _ => {
  console.log('Start');
  
  for (let index = 0; index <p>在<code>for</code> #循環中,過上使用<code>getNumFruit</code>來取得每個水果的數量,並將數量列印到控制台。 </p><p>由於<code>getNumFruit</code>傳回一個<code>promise</code>,我們使用 <code>await</code> 來等待結果的回傳並列印它。 </p><pre class="brush:php;toolbar:false">const forLoop = async _ => {
  console.log('start');

  for (let index = 0; index <p>當使用<code>await</code>時,希望JavaScript暫停執行,直到等待 promise 回傳處理結果。這意味著<code>for</code>循環中的<code>await</code> 應該按順序執行。 </p><p>結果就如你所預料的。 </p><pre class="brush:php;toolbar:false">“Start”;
“Apple: 27”;
“Grape: 0”;
“Pear: 14”;
“End”;

JavaScript循環如何使用 async/await?需要注意些什麼?

這種行為適用於大多數迴圈(例如whilefor-of迴圈)…

但是它不能處理需要回呼的循環,例如forEachmapfilterreduce。在接下來的幾節中,我們將研究await 如何影響forEach、map和filter

在 forEach 迴圈中使用 await

首先,使用 forEach 對陣列進行遍歷。

const forEach = _ => {
  console.log('start');

  fruitsToGet.forEach(fruit => {
    //...
  })

  console.log('End')
}

接下來,我們將嘗試使用getNumFruit來取得水果數量。 (注意回呼函數中的async關鍵字。我們需要這個async關鍵字,因為await在回呼函數中)。

const forEachLoop = _ => {
  console.log('Start');

  fruitsToGet.forEach(async fruit => {
    const numFruit = await getNumFruit(fruit);
    console.log(numFruit)
  });

  console.log('End')
}

我期望控制台列印以下內容:

“Start”;
“27”;
“0”;
“14”;
“End”;

但實際結果是不同的。在forEach迴圈中等待回傳結果之前,JavaScrip先執行了 console.log('End')。

實際控制台列印如下:

‘Start’
‘End’
‘27’
‘0’
‘14’

JavaScript循環如何使用 async/await?需要注意些什麼?

JavaScript 中的forEach不支援promise 感知,也支援asyncawait,所以不能在forEach 上使用await

在map 中使用await

如果在map中使用await, map 始終傳回promise數組,這是因為非同步函數總是傳回promise

const mapLoop = async _ => {
  console.log('Start')
  const numFruits = await fruitsToGet.map(async fruit => {
    const numFruit = await getNumFruit(fruit);
    return numFruit;
  })
  
  console.log(numFruits);

  console.log('End')
}
      

“Start”;
“[Promise, Promise, Promise]”;
“End”;

JavaScript循環如何使用 async/await?需要注意些什麼?

如果你在map 中使用awaitmap 總是回傳promises ,你必須等待promises 陣列得到處理。或透過await Promise.all(arrayOfPromises)來完成此操作。

const mapLoop = async _ => {
  console.log('Start');

  const promises = fruitsToGet.map(async fruit => {
    const numFruit = await getNumFruit(fruit);
    return numFruit;
  });

  const numFruits = await Promise.all(promises);
  console.log(numFruits);

  console.log('End')
}

運行結果如下:

JavaScript循環如何使用 async/await?需要注意些什麼?

如果你願意,可以在promise 中處理回傳值,解析後的將是返回的值。

const mapLoop = _ => {
  // ...
  const promises = fruitsToGet.map(async fruit => {
    const numFruit = await getNumFruit(fruit);
    return numFruit + 100
  })
  // ...
}
 
“Start”;
“[127, 100, 114]”;
“End”;

在 filter 迴圈中使用 await

#當你使用filter時,希望篩選具有特定結果的陣列。假設過濾數量大於20的陣列。

如果你正常使用filter (沒有await),如下:

const filterLoop =  _ => {
  console.log('Start')

  const moreThan20 =  fruitsToGet.filter(async fruit => {
    const numFruit = await fruitBasket[fruit]
    return numFruit > 20
  })
  
  console.log(moreThan20) 
  console.log('END')
}

運行結果

Start
["apple"]
END

filter 中的await不會以相同的方式運作。事實上,它根本不起作用。

const filterLoop = async _ => {
  console.log('Start')

  const moreThan20 =  await fruitsToGet.filter(async fruit => {
    const numFruit = fruitBasket[fruit]
    return numFruit > 20
  })
  
  console.log(moreThan20) 
  console.log('END')
}


// 打印结果
Start
["apple", "grape", "pear"]
END

JavaScript循環如何使用 async/await?需要注意些什麼?

為什麼會發生這種情況?

當在filter 回呼中使用await時,回調總是一個promise。由於promise 總是真的,數組中的所有項目都通過filter 。在filter 使用 await類別以下這段程式碼

const filtered = array.filter(true);

filter使用 await 正确的三个步骤

  1. 使用map返回一个promise 数组
  2. 使用 await 等待处理结果
  3. 使用 filter 对返回的结果进行处理
const filterLoop = async _ => {
  console.log('Start');

  const promises = await fruitsToGet.map(fruit => getNumFruit(fruit));
 
  const numFruits = await Promise.all(promises);

  const moreThan20 = fruitsToGet.filter((fruit, index) => {
    const numFruit = numFruits[index];
    return numFruit > 20;
  })

  console.log(moreThan20);
  console.log('End')
}

JavaScript循環如何使用 async/await?需要注意些什麼?

在 reduce 循环中使用 await

如果想要计算 fruitBastet中的水果总数。 通常,你可以使用reduce循环遍历数组并将数字相加。

const reduceLoop = _ => {
  console.log('Start');

  const sum = fruitsToGet.reduce((sum, fruit) => {
    const numFruit = fruitBasket[fruit];
    return sum + numFruit;
  }, 0)

  console.log(sum)
  console.log('End')
}

运行结果:

JavaScript循環如何使用 async/await?需要注意些什麼?

当你在 reduce 中使用await时,结果会变得非常混乱。

 const reduceLoop = async _ => {
  console.log('Start');

  const sum = await fruitsToGet.reduce(async (sum, fruit) => {
    const numFruit = await fruitBasket[fruit];
    return sum + numFruit;
  }, 0)

  console.log(sum)
  console.log('End')
}

JavaScript循環如何使用 async/await?需要注意些什麼?

[object Promise]14 是什么 鬼??

剖析这一点很有趣。

  1. 在第一次遍历中,sum0numFruit27(通过getNumFruit(apple)的得到的值),0 + 27 = 27
  2. 在第二次遍历中,sum是一个promise。 (为什么?因为异步函数总是返回promises!)numFruit0.promise 无法正常添加到对象,因此JavaScript将其转换为[object Promise]字符串。 [object Promise] + 0object Promise] 0
  3. 在第三次遍历中,sum 也是一个promisenumFruit14. [object Promise] + 14[object Promise] 14

解开谜团!

这意味着,你可以在reduce回调中使用await,但是你必须记住先等待累加器!

const reduceLoop = async _ => {
  console.log('Start');

  const sum = await fruitsToGet.reduce(async (promisedSum, fruit) => {
    const sum = await promisedSum;
    const numFruit = await fruitBasket[fruit];
    return sum + numFruit;
  }, 0)

  console.log(sum)
  console.log('End')
}

JavaScript循環如何使用 async/await?需要注意些什麼?

但是从上图中看到的那样,await 操作都需要很长时间。 发生这种情况是因为reduceLoop需要等待每次遍历完成promisedSum

有一种方法可以加速reduce循环,如果你在等待promisedSum之前先等待getNumFruits(),那么reduceLoop只需要一秒钟即可完成:

const reduceLoop = async _ => {
  console.log('Start');

  const sum = await fruitsToGet.reduce(async (promisedSum, fruit) => {
    const numFruit = await fruitBasket[fruit];
    const sum = await promisedSum;
    return sum + numFruit;
  }, 0)

  console.log(sum)
  console.log('End')
}

1JavaScript循環如何使用 async/await?需要注意些什麼?

这是因为reduce可以在等待循环的下一个迭代之前触发所有三个getNumFruit promise。然而,这个方法有点令人困惑,因为你必须注意等待的顺序。

在reduce中使用wait最简单(也是最有效)的方法是

  1. 使用map返回一个promise 数组
  2. 使用 await 等待处理结果
  3. 使用 reduce 对返回的结果进行处理

    const reduceLoop = async _ => {
     console.log('Start');

    const promises = fruitsToGet.map(getNumFruit);
     const numFruits = await Promise.all(promises);
     const sum = numFruits.reduce((sum, fruit) => sum + fruit);

    console.log(sum)
     console.log('End')
    }

这个版本易于阅读和理解,需要一秒钟来计算水果总数。

1JavaScript循環如何使用 async/await?需要注意些什麼?

从上面看出来什么

  1. 如果你想连续执行await调用,请使用for循环(或任何没有回调的循环)。
  2. 永远不要和forEach一起使用await,而是使用for循环(或任何没有回调的循环)。
  3. 不要在 filterreduce 中使用 await,如果需要,先用 map 进一步骤处理,然后在使用 filterreduce 进行处理。

原文地址:https://medium.com/free-code-camp/javascript-async-and-await-in-loops-30ecc5fb3939 

更多编程相关知识,请访问:编程学习网站!!

以上是JavaScript循環如何使用 async/await?需要注意些什麼?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除