Привет. На этой неделе я расскажу вам о том, как устроен асинхронный код на языке JavaScript. Все примеры, которые мы вам показывали в наших лекциях ранее, были написаны в синхронном стиле, то есть если у нас было описано несколько функций, и мы вызывали их одну за другой, то мы всегда знали, какую строчку кода исполняет интерпретатор. Все было достаточно просто и понятно. С асинхронным кодом все немного сложнее. Для того, чтобы понять, как работает асинхронный код, давайте рассмотрим две новые структуры: стек вызовов и очередь событий. Стек вызовов — это структура данных, которой оперирует интерпретатор. Как только мы начинаем исполнять наш код, интерпретатор складывает в стек вызовов анонимную функцию. Как только выполнение нашего кода заканчивается, анонимная функция выталкивается из стека. Если при выполнении нашего кода встречается вызов другой функции, то интерпретатор складывает эту функцию в стек вызовов. В нашем случае при выполнении анонимной функции мы встретили вызов функции toCheerUp. Соответственно, мы положили ее в стек вызовов и начали ее интерпретировать. В функции toCheerUp мы встречаем вызов другой функции — prepareCoffee. Соответственно, мы складываем эту функцию в стек вызовов и идем ее интерпретировать. В функции prepareCoffee мы вызываем функцию toStove, которая в свою очередь выводит на консоль некоторую строку. Как только интерпретация этой функции заканчивается, функция пропадает из стека, и мы возвращаемся к предыдущей функции, в нашем случае — к функции prepareCoffee. Вместе с вызовом функции toStove заканчивается и вызов функции prepareCoffee. Значит, и она тоже достается из стека. Аналогичным образом мы поступаем с двумя оставшимися функциями. Они пропадают из стека, и выполнение нашего кода заканчивается. Вы уже неоднократно могли сталкиваться со стеком вызовов, когда допускали неконтролируемые исключения. Давайте рассмотрим следующий пример. Перепишем функцию toStove таким образом, чтобы она выбрасывала исключение при помощи метода throw. Таким образом, если мы дойдем по стеку вызовов до функции toStove, и она выбросит исключение, то в консоли мы увидим следующую ошибку: мы не можем поставить кофе на плиту, потому что нет электричества, и увидим стек вызовов, где ошибка произошла. Еще одна структура данных, которая понадобится нам для понимания работы асинхронности в JavaScript — это очередь событий. Как только интерпретатор начинает исполнять наш код, он складывает анонимную функцию не сразу в стек вызовов, а для начала помещает ее в очередь событий. Далее работа очереди и стека согласуются следующим образом: как только стек вызовов пустеет, он достает первую функцию из очереди событий. В нашем случае это анонимная функция. То есть она как бы перекладывается из очереди событий в стек вызовов. Далее мы начинаем интерпретировать код, который написан в анонимной функции. Встречаем вызов функции toCheerUp, она вызывает функцию prepareCoffee, она вызывает функцию toStove и т.д. Мы складываем функции в стек вызовов, как только функции завершаются, мы достаем их из стека вызовов. Как только стек опустел, наша программа снова обращается к очереди событий, и если там есть новая функция, то она перекладывает ее в стек вызовов и начинает ее исполнять. В нашем случае в очереди событий ничего нет, значит, наша программа завершится. Таким образом работает цикл событий. В стек вызовов функция может попадать из очереди событий или если мы вызываем код внутри нашей функции. А в очередь событий функция попадает не только когда мы начинаем выполнять наш код. Есть еще несколько способов записать функцию в очередь событий, и их мы рассмотрим в следующих видео.