Интенсивное чтение на этой неделе«Побег из асинхронного/ожидающего ада».
1. Введение
Наконец, жаловались на async/await. Адитья Агарвал считает, что синтаксис async/await доставляет нам новые неприятности.
На самом деле автор давно чувствовал, что что-то не так, и наконец кто-то сказал правду, async/await может вызвать проблемы.
2 Обзор
Вот современный интерфейсный код везде:
(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
})();
С самим синтаксисом await проблем нет, но иногда пользователь может использовать его неправильно. когдаpizzaData
а такжеdrinkData
Когда между ними нет зависимостей, последовательное ожидание максимально удвоит время выполнения.getPizzaData
время работы, потому чтоgetPizzaData
а такжеgetDrinkData
должны выполняться параллельно.
Вернемся к аду обратных вызовов, на который мы жаловались, хотя код уродлив, добавление хотя бы двух строк кода обратного вызова не приведет к блокировке.
Кажется, что упрощение синтаксиса приносит проблемы с производительностью и напрямую влияет на пользовательский опыт.
Правильный подход должен заключаться в одновременном выполнении функции, а затем в ожидании возвращаемого значения, чтобы асинхронные функции могли выполняться параллельно:
(async () => {
const pizzaPromise = selectPizza();
const drinkPromise = selectDrink();
await pizzaPromise;
await drinkPromise;
orderItems(); // async call
})();
или использоватьPromise.all
Делает код более читабельным:
(async () => {
Promise.all([selectPizza(), selectDrink()]).then(orderItems); // async call
})();
Кажется, что не нужно ждать произвольно, это может снизить производительность вашего кода.
3 Интенсивное чтение
Тщательно подумав о том, почему злоупотребляют async/await, я думаю, что его функция относительно нелогична.
Во-первых, async/await — это действительно синтаксический сахар, и его функция заключается только в том, чтобы сделать написание кода более удобным. Не глядя на его синтаксис или особенности, просто по трем словам синтаксического сахара видно, что он должен ограничивать некоторые возможности.
Например, мы используем тег html для инкапсуляции компонента, что приносит удобство и в то же время его функция должна быть подмножеством html. Другой пример, некий колесный брат считает, что API определенного компонента слишком сложный, поэтому на его основе инкапсулируется синтаксический сахар, большинство из нас может подумать, что это удобство достигается за счет принесения в жертву некоторых функций.
Функциональная целостность и простота использования всегда конкурировали друг с другом.Различные версии многих фреймворков с открытым исходным кодом — это почти результат смешения функциональной целостности и удобства в разных пропорциях.
Итак, вернемся к async/await. Проблема, которую он решает, — это катастрофа ада обратных вызовов:
a(() => {
b(() => {
c();
});
});
Чтобы уменьшить влияние слишком большого количества вложенных структур на мозг, async/await решил написать:
await a();
await b();
await c();
Хотя уровень согласован, он все же логически вложен.Разве это не еще одна степень увеличения нагрузки на мозг? И это преобразование невидимо, поэтому мы часто игнорируем его, что приводит к злоупотреблению синтаксическим сахаром.
понимать синтаксический сахар
Хотя правильно понимать реальный эффект от async/await противочеловечно, чтобы очистить структуру кода и не допустить написания низкопроизводительного кода, необходимо серьезно понимать изменения, привносимые async/await.
Прежде всего, async/await может реализовать только часть функций, поддерживаемых обратными вызовами, то есть удобно иметь дело только с вложенными сценариями. В других сценариях вам нужно использовать некоторые мозги.
Например, две пары обратных вызовов:
a(() => {
b();
});
c(() => {
d();
});
Если она написана следующим образом, хотя функция гарантированно непротиворечива, она становится наименее эффективным методом выполнения:
await a();
await b();
await c();
await d();
Поскольку он переводится в обратный вызов, он становится:
a(() => {
b(() => {
c(() => {
d();
});
});
});
Однако мы обнаружили, что в исходном коде функцияc
С участиемa
выполняться одновременно, но синтаксис async/await заставляет насb
После выполнения выполнитьc
.
Поэтому, когда мы это осознаем, мы можем немного оптимизировать производительность:
const resA = a();
const resC = c();
await resA;
b();
await resC;
d();
Но на самом деле эта логика не может добиться эффекта обратного вызова, хотяa
а такжеc
выполняются одновременно, ноd
Изначально просто ждалc
закончено, теперь, еслиa
соотношение времени выполненияc
долго становится:
a(() => {
d();
});
Кажется, что он полностью изолирован только от двух функций:
(async () => {
await a();
b();
})();
(async () => {
await c();
d();
})();
или использоватьPromise.all
:
async function ab() {
await a();
b();
}
async function cd() {
await c();
d();
}
Promise.all([ab(), cd()]);
Это страшная вещь, которую я хочу выразить. Метод обратного вызова — такой простой процедурный код, и он еще более ужасен, чем ад обратных вызовов.
И большая часть кода сцены очень сложная, синхронизация и ожидание перемешаны между собой, часто сложно понять контекст и правильно оптимизировать производительность. Но почему мы сами роем себе ямы, а потом их засыпаем? Во многих случаях это приведет к тому, что вы забудете заполнить.
Первоначальный автор далPromise.all
Метод упрощает логику, но автор считает, что можно повысить читабельность кода, не игнорируя синтаксис async/await и при необходимости соответствующим образом используя обратные вызовы.
4 Резюме
Ад обратного вызова async/await напоминает нам не слишком полагаться на новые функции, иначе эффективность выполнения кода может снизиться, что повлияет на пользовательский опыт. При этом автор считает, что не стоит чрезмерно использовать новые фичи для исправления проблем, вызванных новыми фичами, что приведет к снижению читабельности кода.
Когда я открывал старый код в период, когда редукс только стал популярным, я видел много кода, который был переходной абстракцией и использовался для использования, я просто разделил логику, которую можно записать в две строки кода, на три файла, разбросанных по шесть разных строк, мне пришлось использовать поиск строк, чтобы найти подсказки, и, наконец, я обнаружил, что этот абстрактный код использовался только один раз во всем проекте.
Есть только одна возможность написать такой код — выпить весь куриный бульон, который дает редукс, на одном дыхании в случае умственного оцепенения.
Точно так же, как async/await hell, видя такой редукс-код, я думаю, что он намного уступает коду jquery, написанному так называемым старым интерфейсом, который не идет в ногу со временем.
Качество кода определяется мышлением, а не фреймворком или синтаксисом.Async/await хорош, но в меру.
еще 5 обсуждений
Адрес обсуждения:Интенсивное чтение «Побег из ада async/await» · Выпуск №82 · dt-fe/weekly
Если вы хотите принять участие в обсуждении, пожалуйста,кликните сюда, с новыми темами каждую неделю, выходящими по выходным или понедельникам.