JS: Асинхронное программирование
Теория: Упорядочивание асинхронных операций
Асинхронное программирование помогает эффективно использовать вычислительные ресурсы. Но создает сложности там, где изначально было просто. В первую очередь это касается порядка выполнения (flow). Предположим, что перед нами стоит задача прочитать содержимое двух файлов и записать в третий (объединение файлов).
Вся задача сводится к последовательному выполнению трёх операций, так как записать новый файл мы можем лишь тогда, когда прочитаем данные первых двух. Упорядочить подобный код можно лишь одним способом: каждая последующая операция должна запускаться внутри колбека предыдущей. Тогда мы построим нужную цепочку вызовов:
В реальных программах количество операций может быть значительно больше: например, десятки — и тогда у вас получится лесенка из 10-и вложенных вызовов. Подобное свойство асинхронного кода нередко называют Callback Hell («ад колбеков») из-за большого числа вложенных колбеков, которые очень затрудняют анализ программы. Кто-то даже сделал сайт http://callbackhell.com/, на котором разбирается эта проблема и приводится вот такой код:
В некоторых случаях заранее неизвестно, сколько надо будет выполнить операций. Например, может понадобиться прочитать содержимое директории и посмотреть, кто владелец каждого файла (его uid). Если бы код был синхронный, то наше решение выглядело бы так:
Последовательный код прост и понятен, каждая следующая строчка выполняется после того, как закончится предыдущая, а в map каждый элемент обрабатывается гарантированно последовательно.
С асинхронным кодом возникают вопросы. И если чтение директории — операция, которую мы сделаем в любом случае, то как описать анализ файлов, ведь их может быть любое количество. К сожалению, без использования готовых абстракций, упрощающих данную задачу, мы получим много сложного кода. Настолько сложного, что в реальной жизни так лучше никогда не делать, этот код приводится только в образовательных целях.
Общий принцип такой: формируется специальная функция (readFileStat), которая рекурсивно вызывается, передавая себя в функцию stat. С каждым новым вызовом она отрабатывает один файл и уменьшает массив items, в котором содержатся еще необработанные файлы. Вторым параметром она аккумулирует (собирает) получившийся результат, который в конце передаётся в колбек cb (переданный вторым аргументом функции getFileOwners). Пример выше реализует итеративный процесс, построенный на рекурсивных функциях. Чтобы лучше понять код выше, попробуйте скопировать его к себе на компьютер и позапускайте с разными аргументами, предварительно расставив отладочный вывод внутри неё.





