JS: Асинхронное программирование
Теория: Цепочка промисов
Даже при использовании промисов не всегда понятно, как структурировать асинхронный код. В этом уроке мы разберём некоторые полезные практики, делающие его проще для написания и анализа. Возьмём уже знакомую нам задачку по объединению двух файлов.
Сейчас мы проведём серию рефакторингов и получим в результате код, который является каноническим при работе с промисами. Итак, первая версия:
Хорошая новость — код стал понятнее и уменьшился в объёме. К тому же, из него целиком ушла обработка ошибок, так как промисы обрабатывают их автоматически и, если вызывающий код захочет их перехватывать, то сделает это самостоятельно через метод catch(). Но есть и плохая новость — код всё еще структурирован как колбеки, «лесенкой». В этом коде не учитывается свойство промисов, связанное с возвратом из then().
Эта версия совсем плоская, именно к такому коду нужно стремиться в промисах. Но она таит в себе одну проблему. Если где-то в цепочке ниже нужны данные, которые были получены сверху, то придется протаскивать их сквозь всю цепочку. В примере выше это результат чтения первого файла. Переменная data1 недоступна в том месте, где происходит запись в файл. Основной выход из данной ситуации — создание переменных, через которые данные будут прокинуты дальше:
Уже не так красиво, но всё еще плоско. Преимущество такого подхода становится всё более и более очевидным с увеличением количества промисов. Тем более далеко не всегда нужно передавать данные дальше.
Контроль асинхронных операций
Частая ошибка при работе с промисами – потеря контроля. Посмотрите на немного измененный код из примеров выше:
Этот код хоть и сработает во многих ситуациях, все же содержит серьезную ошибку. Промис внутри константы promise не возвращается наружу. Цепочка промисов прервалась. Любая ошибка в этом промисе пройдет незамеченной для внешнего кода. Нет гарантии, что этот промис вообще успеет выполниться к тому моменту, когда этого будет ожидать вызывающий код.
Нарушение непрерывности (контроля) асинхронных операций частая ошибка даже у опытных программистов. В некоторых ситуациях ошибка заметна сразу, в других код начинает вести себя странно: часть запусков проходит без проблем, другая падает со странными ошибками.
Чтобы этого не происходило, нужно всегда убеждаться в непрерывности асинхронных операций. Завершение любой операции должно приводить к какой-то реакции, кто-то всегда должен ждать этого момента.
Динамическая цепочка
Иногда количество асинхронных операций заранее неизвестно, но они должны выполняться строго по очереди. Эту задачу можно решить, используя циклы или свертку. Какой бы способ не был выбран, сам принцип построения цепочки не поменяется. Цепочка промисов это всегда then().then().then()....
Единственный нюанс, который нужно учесть – начальный промис, с которого начнёт строиться цепочка. Если такого промиса нет, то его можно создать используя функцию Promise.resolve(). Она возвращает промис, который ничего не делает, но с него можно начинать свертку. Ниже пример, в котором последовательно читается набор файлов и возвращается массив их содержимого:





