исходный адресHow to escape async/await hell
async/await
Освободил нас от ада обратного звонка, но люди также критиковали его, потому что это привело кasync/await
Рождение ада.
В этой статье я постараюсь объяснить, что такоеasync/await
Черт, плюс я поделюсь некоторыми способами избежать их.
Что такое асинхронный/ожидающий ад?
Когда мы пишем асинхронный код на JavaScript, люди часто добавляют перед одним вызовом функции за другимawait
Это может вызвать проблемы с производительностью, поскольку в обычном случае выполнение одного оператора зависит не от выполнения предыдущего оператора, а из-за добавленияawait
ключевое слово, вам все равно нужно дождаться завершения предыдущего оператора, прежде чем выполнять оператор.
Пример асинхронного/ожидающего ада.
Предположим, вы пишете код для покупки пиццы и напитков, код выглядит так.
(async () => {
const pizzaData = await getPizzaData() // async call
const drinkData = await getDrinkData() // async call
const chosenPizza = choosePizza() // sync call
const chosenDrink = chooseDrink() // sync call
await addPizzaToCart(chosenPizza) // async call
await addDrinkToCart(chosenDrink) // async call
orderItems() // async call
})()
На первый взгляд, этот код синтаксически корректен и может работать. Однако это не очень хорошая реализация, поскольку он убрал параллельное выполнение. Давайте посмотрим, что делает этот код, чтобы лучше понять, в чем заключается проблема.
объяснять
Оборачиваем этот код в асинхронную немедленно исполняемую функцию, по порядку происходят следующие вещи:
- Получите список пицц.
- Получить список напитков.
- Выберите пиццу из списка пиццы.
- Выберите напиток из списка напитков.
- Добавьте выбранную пиццу в корзину
- Выбор напитков в корзину.
- Подтвердите заказ
Где ошибка?
Как я упоминал ранее, все операторы выполняются построчно, здесь нет параллельного выполнения. Давайте подумаем, почему нам нужно ждать возврата списка пиццы, прежде чем получить список напитков? Мы должны попытаться получить список напитков и пиццы одновременно. Однако, когда нам нужно выбрать пиццу, нам нужно сначала получить список пиццы. То же самое касается напитков.
Таким образом, мы определили, что работа, связанная с пиццей, и работа, связанная с напитком, могут выполняться одновременно, но каждый этап работы, связанной с пиццей, должен выполняться по порядку (последовательное выполнение).
Еще один плохой пример
Этот код JavaScript доставит товары в корзину и отправит запрос на подтверждение заказа.
async function orderItems() {
const items = await getCartItems() // async call
const noOfItems = items.length
for(var i = 0; i < noOfItems; i++) {
await sendRequest(items[i]) // async call
}
}
В этом случае цикл for должен дождаться текущегоsendRequest()
Выполнение завершено, но нам не нужно ждать, мы хотим отправить все запросы как можно быстрее и дождаться их завершения.
Надеюсь, теперь вы ясно понимаете, чтоasync/await
Черт и как сильно они влияют на производительность вашей программы.Теперь я хочу задать вам вопрос
Что, если мы забудем ключевое слово await?
Если вы забыли добавить перед вызовом асинхронной функцииawait
ключевое слово, функция начинает выполняться, что означаетawait
не является необходимым условием для выполнения функции.Эта асинхронная функция вернетpromise
,этоpromise
Мы можем использовать его позже.
(async () => {
const value = doSomeAsyncTask()
console.log(value) // an unresolved promise
})()
В результате компилятор не знает, что нужно дождаться завершения функции, поэтому компилятор выйдет из программы до завершения асинхронной задачи.await
ключевые слова.
promise
Есть интересное свойство, которое можно получить в предыдущем коде.promise
, затем дождитесь этого в коде позадиpromise
Готово. Это изasync/await
Ключ к освобождению из ада.
(async () => {
const promise = doSomeAsyncTask()
const value = await promise
console.log(value) // the actual value
})()
Как вы видете,doSomeAsyncTask()
Обещание возвращается. В это время,doSomeAsyncTask()
Выполнение уже началось, чтобы получить результирующее значение этого промиса, мы можем добавить перед этим промисомawait
, JavaScript тут же остановится и не выполнит следующую строку кода, пока не будет получено возвращаемое значение этого промиса, а затем выполнит следующую строку кода.
Как избежать асинхронного/ожидающего ада?
Вы должны выполнить следующие шаги, чтобы сбежатьasync/await
ад.
Найти все операторы, выполненные после других операторов
В нашем первом примере мы выбираем пиццу и напитки.Итак, мы делаем вывод, что прежде чем выбрать пиццу, нам нужно получить список пицц. Также, прежде чем добавить пиццу в корзину, нам нужно выбрать пиццу. Да Думая, что эти три шага взаимозависимы, мы не можем выполнить следующую задачу, пока не будет завершена предыдущая. Но если мы немного расширим кругозор, то увидим, что выбор пиццы не зависит от выбора напитка, мы можем выбрать и то, и другое, и здесь машины могут быть лучше нас. До сих пор мы обнаружили, что некоторые операторы зависят от выполнения других операторов, а другие — нет.
Интегрируйте взаимозависимые операторы выполнения в асинхронные функции.
Как видим, выберите пиццу, чтобы понадобиться несколько взаимозависимых операторов, таких как получение списка пиццы, выберите одну из пицц, затем добавьте в корзину. Мы должны интегрировать эти утверждения в асинхронной функции. Так что мы получим два асинхронных функции,selectPizza()
а такжеselectDrink()
Выполняйте эти асинхронные функции одновременно.
Мы воспользуемся преимуществами цикла обработки событий для одновременного выполнения этих неблокирующих асинхронных функций.Для достижения этой цели наш обычный метод — сначала вернутьсяpromiseзатем используйтеPromise.allметод.
Давайте исправим этот пример
Основываясь на трех шагах, упомянутых ранее, мы применим их к нашему примеру.
async function selectPizza() {
const pizzaData = await getPizzaData() // async call
const chosenPizza = choosePizza() // sync call
await addPizzaToCart(chosenPizza) // async call
}
async function selectDrink() {
const drinkData = await getDrinkData() // async call
const chosenDrink = chooseDrink() // sync call
await addDrinkToCart(chosenDrink) // async call
}
(async () => {
const pizzaPromise = selectPizza()
const drinkPromise = selectDrink()
await pizzaPromise
await drinkPromise
orderItems() // async call
})()
// 我更喜欢下面这种实现.
(async () => {
Promise.all([selectPizza(), selectDrink()]).then(orderItems) // async call
})()
Теперь мы объединили эти операторы в две функции, в каждой функции выполнение каждого оператора зависит от выполнения предыдущей функции, затем мы выполняем параллельноselectPizza()
а такжеselectDrink()
.
Во втором примере нам нужно решить неизвестное количество промисов, решить эту ситуацию очень просто: мы просто создаем массив и храним в нем промисы, а затем используемPromise.all()
метод, вы можете одновременно ждать, пока все промисы вернут результаты.
async function orderItems() {
const items = await getCartItems() // async call
const noOfItems = items.length
const promises = []
for(var i = 0; i < noOfItems; i++) {
const orderPromise = sendRequest(items[i]) // async call
promises.push(orderPromise) // sync call
}
await Promise.all(promises) // async call
}
// 我更喜欢下面这种实现
async function orderItems() {
const items = await getCartItems() // async call
const promises = items.map((item) => sendRequest(item))
await Promise.all(promises) // async call
}
Мне нравится, что эта статья помогает вам уйти с дорогиasync/await
Ранги основных пользователей и могут помочь вам улучшить производительность вашей программы.
Если вам понравилась эта статья, я надеюсь, что вы можете поставить лайк и добавить ее в закладки.