Перестаньте пытаться сделать async/await главным
Промисы сейчас в тренде
С незапамятных времён разработчики ведут бессмысленные споры. От классического «Табы против пробелов» до вечного «Mac против PC» — мы умеем находить отвлекающие аргументы.
Ответы: Linux и пробелы.
Битва…?
Промисы против Async/Await!
Подождите, это вообще битва? Должна же быть, правда? Мы больше не говорим о колбэках?
Нет, это не битва. В конечном счёте это ещё один потенциальный инструмент в вашем арсенале. Однако, поскольку async/await не заменяет всю функциональность промисов (в частности, Promise.all, .race) ошибочно представлять его как замену.
Многие влиятельные люди продвигают это заблуждение, будто async/await — это замена промисам, которую все так долго ждали.
Подсказка: нет, нет и ещё раз нет.
Недавнее дополнение к VS Code усиливает этот перекос. Как написал @umaar:
Visual Studio Code can now convert your long chains of Promise.then()‘s into async/await! 🎊 Works very well in both JavaScript and TypeScript files. .catch() is also correctly converted to try/catch ✅ pic.x.com/xb39Lsp84V
— Umar Hansa (@umaar) September 28, 2018
Если вы ненавидите промисы и хотите такую функцию рефакторинга, я не виню вас.
Я сопереживаю. Я понимаю.
Я был там. 🤗
Раньше я ненавидел промисы. Сегодня я полностью изменил своё мнение. Промисы великолепны. Они позволяют и побуждают вас использовать преимущества функциональной композиции.
Есть 2 области, на которых я рекомендую сосредоточиться в первую очередь, чтобы улучшить работу с промисами.
#1: Именованные функции!
Избавьтесь от анонимных методов. Использование именованных функций делает код читаемым, как поэма ваших требований.
Рассмотрим распространённый пример:
Выполнение HTTP GET-запроса с помощью fetch:
Антипаттерн
// ❌ Использование анонимных inline-функций 💩fetch(url) .then(response => response.status < 400 ? response : Promise.reject(new Error('Request Failed: ' + response.ststus))) .then(response => response.text())Решение: Именованные методы
// ✅ Поясняется ясность: именованные функцииfetch(url) .then(checkResponse) .then(getText)
// Переиспользуемые функции общего назначенияfunction checkResponse(response) { return response.status < 400 ? response : Promise.reject(new Error('Request Failed: ' + response.ststus))}function getText(response) { return response.text()}Преимущества этого подхода становятся всё очевиднее по мере того, как ваш код становится суше.
Дополнительные ресурсы: Посмотрите мои 1-минутные видео о базовом логировании и продвинутой отладке с использованием этой техники.
#2: Единственная ответственность (функций)
Это звучит обманчиво точно: единственная ответственность.
И всё же это настолько субъективно и произвольно, что иногда даже бессмысленно.
Интересно, что большинство разработчиков считают себя довольно хорошими в применении принципа единственной ответственности к своему коду. Неудивительно: они также считают себя отличными водителями!
Рассмотрим пример, который (невероятно талантливый) Jake Archibald приводит в своей статье об async/await для сайта Google Developers (примечание: 2024, ссылка удалена).
// source: https://developers.google.com/web/fundamentals/primers/async-functionsfunction logInOrder(urls) { // fetch all the URLs const textPromises = urls.map(url => { return fetch(url).then(response => response.text()); });
// log them in order textPromises.reduce((chain, textPromise) => { return chain.then(() => textPromise) .then(text => console.log(text)); }, Promise.resolve());}Единственная ответственность?
Я бы сказал, нет. Что делает logInOrder?
- Проходит циклом по списку
urls - Применяет к ним inline HTTP GET:
- HTTP
fetch - Возвращает текстовое тело ответа
- HTTP
- Добавляет
.then(text => console.log(text))после каждого промиса вtextPromise- Выводит результаты последовательно
В этой единственной функции определено 5 анонимных методов. Как указывает даже Джейк, .reduce здесь слишком сложен. Не имеет смысла вручную писать такие механизмы повсюду в коде. Другими словами, мы не создаём DOM с помощью бесконечных document.createElement(), element.setAttribute() и т.д. Вместо этого мы выбираем лучший инструмент из множества вариантов: вспомогательные/утилитарные функции, библиотеки или фреймворки.
Решение: Функции с единственной ответственностью
Начните с извлечения методов…

Продолжите, заменив .reduce() и logPromise() на Promise.all и ..map()…

Итог
Попробуйте применить эти техники к своему коду! Затем напишите мне в Twitter и расскажите, как прошло. Или если у вас есть вопросы или комментарии, свяжитесь со мной!
Помогите распространить #PromiseTruth и поделитесь этой статьёй. ❤️
