Как правильно использовать асинхронность и ожидание в циклах JS

JavaScript ECMAScript 6

Переводчик: Front-end Xiaozhi

medium.com/free-code — от…

Ставь лайк и смотри, поиск в WeChat【Переезд в мир】Обратите внимание на этого человека, который не имеет большого фабричного прошлого, но имеет восходящий и позитивный настрой. эта статьяGitHub GitHub.com/QQ449245884…Он был включен, статьи были классифицированы, и многие мои документы и учебные материалы были систематизированы.

Все говорили, что нет проекта для написания резюме, поэтому я помог вам найти проект, и это было с бонусом.【Учебник по строительству】.

В этой статье расскажите об использовании циклов if.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 getNumFruit = fruit => {
  return sleep(1000).then(v => fruitBasket[fruit]);
};

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

awaitа такжеgetNumFruitЧтобы получить счет каждого фрукта в Async Function.

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

Использование ожидания в цикле for

Сначала определите массив для хранения фруктов:

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

Прокрутите этот массив:

const forLoop = async _ => {
  console.log('Start');
  
  for (let index = 0; index < fruitsToGet.length; index++) {
    // 得到每个水果的数量
  }

  console.log('End')
}

существуетforпетля, чрезмерное использованиеgetNumFruitЧтобы получить номер каждого фрукта и количество отпечатков в консоль.

из-заgetNumFruitвернутьpromise,Мы используем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 приостановил выполнение, пока не дождется возврата промиса. это означаетforв петлеawaitследует выполнять по порядку.

Результат такой, как вы ожидаете.

“Start”;
“Apple: 27”;
“Grape: 0”;
“Pear: 14”;
“End”;

Это поведение применимо к большинству циклов (например,whileа такжеfor-ofцикл)…

Но он не может обрабатывать циклы, требующие обратных вызовов, напримерforEach,map,filterа такжеreduce. В следующих нескольких разделах мы изучимawaitКак это влияетforEach, карта иfilter.

Использование ожидания в цикле forEach

Во-первых, используйте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Перед ожиданием возврата результата в цикле JavaScript выполняет Console.log («End»).

Фактическая консольная печать выглядит следующим образом:

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

в JavaScriptforEachНе поддерживает осведомленность об обещаниях и не поддерживаетasyncа такжеawait, так быть не можетforEachиспользовать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”;

если тыmapиспользуется вawait,mapвсегда возвращайся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')
}

Результаты приведены ниже:

Если ты хочешь, ты можешьpromiseВозвращаемое значение обрабатывается при синтаксическом анализе, и возвращаемое значение будет значением после синтаксического анализа.

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

Использование ожидания в цикле фильтрации

когда вы используетеfilterКогда вы хотите отфильтровать массив с определенными результатами. Предположим, что нужно отфильтровать массивы с числом больше 20.

если вы используете его в обычном режимеfilter(без ожидания), следующим образом:

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
 

Почему это происходит?

когда вfilterиспользуется в обратном вызовеawait, обратный вызов всегдаpromise. из-заpromiseвсегда верно, все элементы в массиве проходятfilter. существуетfilterиспользоватьawaitкласс следующий код

const filtered = array.filter(true);

существуетfilterиспользоватьawaitправильные три шага

  1. использоватьmapвозвращает массив промисов

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

Использование 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')
}
 

результат операции:

когда вы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')
}
 

[object Promise]14Что за черт? ?

Интересно разобрать это.

  1. В первом обходе,sumдля0.numFruitда27(пройти черезgetNumFruit(apple)полученное значение),0 + 27 = 27.

  2. Во втором обходеsumЯвляетсяpromise. (Почему? Потому что асинхронные функции всегда возвращаютpromises! )numFruitда0.promise не может быть добавлен к объекту обычным образом, поэтому JavaScript преобразует его в[object Promise]нить.[object Promise] + 0даobject Promise] 0.

  3. В третьем обходе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цикл, если вы ждете, прежде чем promiseSumgetNumFruits(),Так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обещать. Однако этот метод немного сбивает с толку, потому что вы должны обращать внимание на порядок ожиданий.

Самый простой (и самый эффективный) способ использовать ожидание в сокращении:

  1. использоватьmapвозвращает массив промисов

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

Эта версия проста для чтения и понимания, и для подсчета общего количества фруктов требуется секунда.

Как это выглядит сверху

  1. Если вы хотите выполнять непрерывноawaitЗвоните, пользуйтесьforцикл (или любой цикл без обратного вызова).

  2. никогда сforEachиспользовать вместеawait, вместо этого используйтеforцикл (или любой цикл без обратного вызова).

  3. Уходитеfilterа такжеreduceиспользуется вawait, если необходимо, используйте сначалаmapдальнейшая обработка, а затем использованиеfilterа такжеreduceдля обработки.

общаться с

Статья постоянно обновляется каждую неделю. Вы можете выполнить поиск «Big Move to the World» в WeChat, чтобы прочитать и обновить ее как можно скорее (на одну или две статьи раньше, чем в блоге). Эта статья находится на GitHub.GitHub.com/QQ449245884…Он был включен, и многие мои документы были разобраны. Добро пожаловать в Звезду и совершенство. Вы можете обратиться в тестовый центр для ознакомления во время собеседования. Кроме того, обратите внимание на паблик-аккаунт и ответьте в фоновом режиме.Благосостояние, вы можете увидеть преимущества, вы знаете.