Для лучшего понимания асинхронности неплохо разобраться с тем, как устроен рантайм (браузер или Node.js) JavaScript. JavaScript изначально появился в браузерах, и к нему предъявлялись особые требования, из-за которых он кардинально отличается от остальных языков программирования. Браузер работает по так называемой событийной модели. Он загружает страницу и ждёт действий от пользователя: клики, набор текста или движение мышкой. А код, загруженный на страницу, реагирует на эти события.

Такая организация взаимодействия невозможна в синхронном коде, у которого есть понятия "запуск" и "завершение" работы. Код в браузере не может завершиться совсем, он проходит стадию инициализации, а затем ждёт событий для реакции на них. Технически это выглядит, как колбек, который соединён с определённым типом события. Когда событие срабатывает, то колбек вызывается.

See the Pen js_asynchronous_programming_event_loop by Hexlet (@hexlet) on CodePen.

Организация асинхронного взаимодействия требует наличия событийного цикла (Event Loop). Он может быть реализован как на уровне языка в виде библиотеки, так и на уровне рантайма, как в случае с JavaScript. Упрощённо цикл событий можно представить себе так:

// Ждем событие
while (queue.waitForMessage()) {
  // Обрабатываем
  queue.processNextMessage();
}
// Возвращаемся к первому шагу

Как только появляется новое событие, оно начинает обрабатываться (тем самым обработчиком, который мы повесили на это событие). Время обработки события может быть довольно большим, но это не значит, что в этот момент браузер зависает и не даёт пользователю работать. Нет, напротив, благодаря тому, что код асинхронный, события могут (и будут) накапливаться в очереди (queue). Именно поэтому в коде выше сообщение проверяется в переменной с именем queue.

Событийный Цикл в
JavaScript

Событийный цикл всегда работает в однопоточном режиме (понимание этой темы кроется в операционных системах). Это значит, что события обрабатываются строго последовательно. Причём, запуск одного события может приводить к выполнению довольно тяжёлого кода, который работает достаточно долго. Такое периодически встречается, когда страницы сайта вдруг начинают подвисать. Проверьте себя, как вы поняли предыдущие темы. В какой момент начнёт обрабатываться следующее событие?

Правильный ответ: В тот момент, когда текущий стек вызовов опустеет. Пока текущий стек не пуст, все остальные ждут его завершения. Исключение составляют Workers, но это отдельная тема.

По теме событийного цикла существует прекрасное видео, в котором много интересных визуализаций. Крайне рекомендую его посмотреть, оно указано в дополнительных материалах.


Дополнительные материалы

  1. Как на самом деле работает асинхронность?
Мы учим программированию с нуля до стажировки и работы. Попробуйте наш бесплатный курс «Введение в программирование» или полные программы обучения по Node, PHP, Python и Java.

Хекслет

Подробнее о том, почему наше обучение работает →