添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

现在,假设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

假设你想使用await和getNumFruit来获取异步函数中每个水果的数量。

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')
> Start

现在假设有一个数组, 要你循环获取

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

在 for 循环中使用 await

const forLoop = async _ => {
  console.log('start');
  for (let index = 0; index < fruitsToGet.length; index ++) {
    const fruit = fruitsToGet[index];
    const numFruit = await getNumFruit(fruit);
    console.log(numFruit);
  console.log('End')

当使用await时,希望JavaScript暂停执行,直到等待 promise 返回处理结果。这意味着for循环中的await 应该按顺序执行。

//结果
Start

这种行为适用于大多数循环(比如while和for-of循环)…

但是它不能处理需要回调的循环,如forEach、map、filter和reduce。在接下来的几节中,我们将研究await 如何影响forEach、map和filter。

在 forEach 循环中使用 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”;
// 实际结果
Start

结论:JavaScript 中的 forEach不支持 promise 感知,也不支持 async 和await,所以不能在 forEach 使用 await 。

在 map 中使用 await

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”;

如果在map中使用await, map 始终返回promise数组,这是因为异步函数总是返回promise。 

 要想得到正确的结果:通过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')
Start
[27, 0 , 14]

在 filter 循环中使用 await

当你使用filter时,希望筛选具有特定结果的数组。假设过滤数量大于20的数组。

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

const filterLoop =  _ => {
  console.log('Start')
  const moreThan20 =  fruitsToGet.filter(async fruit => {
    const numFruit = fruitBasket[fruit]
    return numFruit > 20
  console.log(moreThan20) 
  console.log('END')
// 打印结果
Start
["apple"]

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","grape","pear"]

当在filter 回调中使用await时,回调总是一个promise。由于promise 总是真的,数组中的所有项都通过filter 。在filter 使用 await类以下这段代码

const filtered = array.filter(true);

在filter使用 await 正确的三个步骤

使用map返回一个promise 数组

使用 await 等待处理结果

使用 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')

在 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')
> Start

当你在 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')
> Start
> [object Promise]14

 剖析这一点很有趣。

  •  在第一次遍历中,sum为0。numFruit是27(通过getNumFruit(apple)的得到的值),0 + 27 = 27。 
  • 在第二次遍历中,sum是一个promise。 (为什么?因为异步函数总是返回promises!)numFruit是0.promise 无法正常添加到对象,因此JavaScript将其转换为[object Promise]字符串。 [object Promise] + 0 是object Promise] 0。 
  • 在第三次遍历中,sum 也是一个promise。 numFruit是14. [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')
    

    但是这种 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')
    

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

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

    使用map返回一个promise 数组 

    使用 await 等待处理结果 

    使用 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')

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