Автор оригинала: FreeCodeCamp Community Member.
Базовый async
и ждать
это просто. Вещи получают немного сложнее, когда вы пытаетесь использовать ждать
в петлях.
В этой статье я хочу поделиться некоторыми GotChas, чтобы посмотреть, если вы намерены использовать ждать
в петлях.
Прежде чем вы начнете
Я собираюсь полагать, что вы знаете, как использовать async
и ждать
Отказ Если вы этого не сделаете, прочитайте Предыдущая статья ознакомиться перед продолжением.
Подготовка примера
Для этой статьи, скажем, вы хотите получить количество фруктов из корзины фруктов.
const fruitBasket = { apple: 27, grape: 0, pear: 14 };
Вы хотите получить количество каждого фрукта от фруктовогоbasket. Чтобы получить количество фруктов, вы можете использовать getnumfruit
функция.
const getNumFruit = fruit => { return fruitBasket[fruit]; }; const numApples = getNumFruit("apple"); console.log(numApples); // 27
Теперь, скажем, Формубаскрет
Живет на удаленном сервере. Доступ к этому занимает одну секунду. Мы можем издеваться над этой одной секундой задержки с тайменами. (Пожалуйста, обратитесь к предыдущей статье Если у вас возникли проблемы с пониманием кода тайм-аута).
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
Наконец, скажем, вы хотите использовать ждать
и 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"); };
С этим мы можем начать смотреть на ждать
в петлях.
Ждать в цикле
Допустим, у нас есть массив фруктов, которые мы хотим получить от фруктовой корзины.
const fruitsToGet = ["apple", "grape", "pear"];
Мы собираемся петлю через этот массив.
const forLoop = async _ => { console.log("Start"); for (let index = 0; index < fruitsToGet.length; index++) { // Get num of each fruit } console.log("End"); };
В форуме мы будем использовать getnumfruit
получить количество каждого фруктов. Мы также регистрируем номер в консоль.
С getnumfruit
Возвращает обещание, мы можем ждать
разрешенное значение перед входом в систему.
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"); };
Когда вы используете ждать
Вы ожидаете, что JavaScript приостановка казни, пока ожидаемое обещание не будет решено. Это значит ждать
S в цепном петле должно быть выполнено последовательно.
В результате вы ожидаете.
"Start"; "Apple: 27"; "Grape: 0"; "Pear: 14"; "End";
Это поведение работает с большинством петель (например в то время как
и для
петли) …
Но это не будет работать с петлями, которые требуют обратного вызова. Примеры таких петель, которые требуют загрязнения, включают foreach
, карта
, Фильтр
и Уменьшить
Отказ Мы посмотрим на то, как ждать
влияет на foreach
, карта
и Фильтр
в следующих нескольких разделах.
Жду петли Foreach
Мы сделаем то же самое, что и в примере для петли. Во-первых, давайте петли через массив фруктов.
const forEachLoop = _ => { console.log("Start"); fruitsToGet.forEach(fruit => { // Send a promise for each fruit }); console.log("End"); };
Далее мы постараемся получить количество фруктов с getnumfruit
Отказ (Обратите внимание на async
Ключевое слово в функции обратного вызова. Нам нужно это async
Ключевое слово, потому что a ждать
находится в функции обратного вызова).
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";
Но фактический результат другой. JavaScript продолжает звонить Console.log ('end')
До того, как обещания в Foreach Loop решаются.
Консольные журналы в этом порядке:
'Start' 'End' '27' '0' '14'
JavaScript это делает это потому, что foreach
не обещается Это не может поддерживать async
и ждать
Отказ Вы _cannot_ использовать ждать
в foreach
Отказ
Жду с картой
Если вы используете ждать
в оформлении карта ,
карта всегда будет вернуть массив обещания. Это потому, что асинхронные функции всегда возвращают обещания.
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";
С карта
Всегда возвращайте обещания (если вы используете ждут
), вы должны ждать, пока не будет разрешен на массив обещаний. Вы можете сделать это с ждать обещания. Все (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]"; "End";
Вы можете манипулировать значением, которое вы возвращаете в свои обещания, если хотите. Разрешенные значения будут возвращаться вами значениями.
const mapLoop = async _ => { // … const promises = fruitsToGet.map(async fruit => { const numFruit = await getNumFruit(fruit); // Adds onn fruits before returning return numFruit + 100; }); // … }; "Start"; "[127, 100, 114]"; "End";
Ждут с фильтром
Когда вы используете Фильтр
, вы хотите отфильтровать массив с определенным результатом. Допустим, вы хотите создать массив с более чем 20 фруктами.
Если вы используете Фильтр
Обычно (без ждут), вы будете использовать это так:
// Filter if there's no await const filterLoop = _ => { console.log('Start') const moreThan20 = await fruitsToGet.filter(fruit => { const numFruit = fruitBasket[fruit] return numFruit > 20 }) console.log(moreThan20) console.log('End') }
Вы ожидаете Morethan20
Содержать только яблоки, потому что есть 27 яблок, но есть 0 винограда и 14 груш.
"Start"["apple"]; ("End");
ждать
в Фильтр
не работает так же. На самом деле, это совсем не работает. Вы получаете нефильтрованный массив обратно …
const filterLoop = _ => { console.log('Start') const moreThan20 = await fruitsToGet.filter(async fruit => { const numFruit = getNumFruit(fruit) return numFruit > 20 }) console.log(moreThan20) console.log('End') } "Start"[("apple", "grape", "pear")]; ("End");
Вот почему это происходит.
Когда вы используете ждать
в оформлении Фильтр Обратный вызов, обратный вызов всегда обещание. Поскольку обещания всегда являются правдой, все элемент в массиве проходит фильтр. Писать
ждать в оформлении Фильтр
это как написать этот код:
// Everything passes the filter… const filtered = array.filter(true);
Есть три шага для использования ждать
и Фильтр
должным образом:
1. Используйте карта
Чтобы вернуть множество обещаний
2. ждать
Массив обещаний
3. Фильтр
разрешенные значения
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"); }; Start["apple"]; End;
Жду с уменьшением
Для этого, скажем, вы хотите узнать общее количество фруктов в фрукте. Обычно вы можете использовать Уменьшить
Чтобы петить через массив и суммировать номер.
// Reduce if there's no await 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"); };
Вы получите в общей сложности 41 фрукты. (27 + 0 +).
"Start"; "41"; "End";
Когда вы используете ждать
С уменьшением, результаты получают чрезвычайно грязные.
// Reduce if we await getNumFruit const reduceLoop = async _ => { console.log("Start"); const sum = await fruitsToGet.reduce(async (sum, fruit) => { const numFruit = await getNumFruit(fruit); return sum + numFruit; }, 0); console.log(sum); console.log("End"); }; "Start"; "[object Promise]14"; "End";
Какие?! [Объектное обещание] 14
?!
Рассекать это интересно.
- В первой итерации
сумма
это0
ОтказNumfruit
27 (разрешенное значение отgetnumfruit («Apple»)
).0 + 27
27. - Во второй итерации
сумма
это обещание. (Почему? Потому что асинхронные функции всегда возвращают обещания!)Numfruit
0. Обещание не может быть добавлено к объекту нормально, поэтому JavaScript преобразует его на[Обещание объекта]
нить.[Объектное обещание] + 0
это[Обещание объекта] 0
- В третьей итерации
сумма
также обещание.Numfruit
это14
Отказ[Объектное обещание] + 14
это[Объектное обещание] 14
Отказ
Тайна решена!
Это значит, вы можете использовать ждать
в оформлении Уменьшить Обратный вызов, но вы должны помнить
ждать Аккумулятор сначала!
const reduceLoop = async _ => { console.log("Start"); const sum = await fruitsToGet.reduce(async (promisedSum, fruit) => { const sum = await promisedSum; const numFruit = await getNumFruit(fruit); return sum + numFruit; }, 0); console.log(sum); console.log("End"); }; "Start"; "41"; "End";
Но … как вы можете видеть из GIF, это занимает довольно долго ждать
все. Это происходит потому, что redeleop
должен ждать Промышленность
быть завершенным для каждой итерации.
Есть способ ускорить петлю уменьшения. (Я узнал об этом благодаря …| Тима Оксли . Если вы жду getnumfruits ( ) сначала до
ждут пообщаю ,
reeweloop занимает только одну секунду, чтобы завершить:
const reduceLoop = async _ => { console.log("Start"); const sum = await fruitsToGet.reduce(async (promisedSum, fruit) => { // Heavy-lifting comes first. // This triggers all three getNumFruit promises before waiting for the next iteration of the loop. const numFruit = await getNumFruit(fruit); const sum = await promisedSum; return sum + numFruit; }, 0); console.log(sum); console.log("End"); };
Это работает, потому что Уменьшить
может выстрелить все три getnumfruit
Обещает до ожидания следующей итерации петли. Тем не менее, этот метод слегка сбивает с толку, поскольку вы должны быть осторожны на заказ, вы ждать
вещи.
Самый простой (и самый эффективный способ) использовать ждать
В уменьшении состоит в том, чтобы:
1. Используйте карта
Чтобы вернуть множество обещаний
2. ждать
Массив обещаний
3. Уменьшить
разрешенные значения
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. Если вы хотите выполнить ждать
звонит в серии, используйте для петли
(или любой цикл без обратного вызова).
2. Никогда не используйте ждать
с foreach
Отказ Используйте для петли
(или любой цикл без обратного вызова) вместо этого.
3. Не ждать
внутри Фильтр
и Уменьшить
Отказ Всегда ждать
Массив обещаний с карта
Тогда Фильтр
или Уменьшить
соответственно.
Эта статья была первоначально опубликована на мой блог Отказ Подпишитесь на мой рассылка Если вы хотите больше статей, чтобы помочь вам стать лучшим разработчиком Frontend.