В Event Loop в языке JavaScript заключается секрет асинхронного программирования. Сам по себе JS является однопоточным, но при использовании нескольких умных структур данных можно создать иллюзию многопоточности (параллельная модель). Как это происходит, расскажем в этой статье.
Код JavaScript работает только в однопоточном режиме. Это означает, что в один и тот же момент может происходить только одно событие. С одной стороны это хорошо, так как такое ограничение значительно упрощает процесс программирования, здесь не возникает проблем параллелизма. Но, как правило, в большинстве браузеров в каждой из вкладок существует свой цикл событий. Среда управляет несколькими параллельными циклами.
Общим знаменателем для всех сред является встроенный механизм, называемый Event Loop JavaScript, который обрабатывает выполнение нескольких фрагментов программы, вызывая каждый раз движок JS.
Существует бесконечный цикл событий, в котором JavaScript движок ожидает свою задачу, выполняет ее и ждет новую. Алгоритм работы движка мы можем видеть при просмотре любой веб-страницы. Он включается в работу тогда, когда необходимо обработать какое-либо событие или скрипт. Схема работы выглядит следующим образом:
Визуально процесс можно изобразить так:
Stack. Источник: Implementing the Stack Data Structure in Javascript
Отслеживание новых событий в цикле:
while(queue.waitForMessage()){ queue.processNextMessage(); }
Если в очереди нет задач, queue.waitForMessage
ожидает их поступления.
Обратите внимание на курсы от наших друзей, школы Hillel и Powercode. Грамотно составленная программа обучения, а также менторство, помогут начинающим разработчикам разобраться во всех деталях и тонкостях.
Все события в браузерах постоянно добавляются в очередь, если они произошли или имеют свой обработчик. setTimeout может добавлять событие в очередь не сразу, а по прошествии указанного времени. Если на данный момент в очереди нет событий, то оно поступит в обработку сразу.
Когда операция setTimeout обрабатывается в стеке, она отправляется соответствующему API, который ожидает до указанного времени, чтобы отправить эту операцию в обработку. Среда управляет несколькими параллельными циклами событий, например, для обработки вызовов API. Веб-воркеры также работают в собственном цикле событий.
Операция отправляется в очередь событий. Следовательно, у нас есть циклическая схема для выполнения асинхронных операций в JavaScript. Сам язык является однопоточным, но API-интерфейсы браузера действуют как отдельные потоки.
Цикл событий постоянно проверяет, пуст ли стек вызовов. Если он пуст, новые функции добавляются из очереди событий. Если это не так, то выполняется текущий вызов функции.
Давайте посмотрим, как отложить выполнение функции до тех пор, пока стек не очистится.
Пример использования setTimeout(() => {}), 0)
заключается в том, чтобы вызвать функцию, но выполнить ее после выполнения всех остальных функций в коде.
Пример:
const bar = () => console.log('bar') const baz = () => console.log('baz') const foo = () => { console.log('foo') setTimeout(bar, 0) baz() } foo()
bar, baz, foo — случайные имена.
При запуске кода сначала вызывается foo(). Внутри foo() мы сначала вызываем setTimeout, передавая bar в качестве аргумента, и инструктируем его таким образом, чтобы он запускался как можно быстрее, передавая 0 в качестве таймера. Затем мы вызываем baz().
Порядок функций в программе:
Event loop. Источник: The JavaScript Event Loop
Почему так происходит?
При вызове setTimeout(), браузер или Node.js запускают таймер. По истечении таймера (в нашем случае мы установили 0) в качестве тайм-аута, функция обратного вызова помещается в очередь событий.
Очередь событий также является местом, где инициированные пользователем события (клики мышью, ввод с клавиатуры и др.) помещаются в очередь до того, как код сможет на них отреагировать.
Event Loop отдает приоритет стеку вызовов. Сначала он обрабатывает все, что находит в стеке вызовов, а когда там ничего не остается, переходит к обработке очереди событий.
setTimeout с аргументом 0 не гарантирует, что обработка будет выполнена мгновенно. Все зависит от того, сколько задач в данный момент находится в очереди. В примере ниже ”message” будет выведена быстрее обработчика callback_1. Объясняется это тем, что задержка представляет собой минимальное время, необходимое среде на выполнение запроса.
(function () { console.log('start'); setTimeout(function callback() { console.log('message from callback'); }); console.log('message'); setTimeout(function callback_1() { console.log('message from callback_1'); }, 0); console.log('finish'); })(); // "start" // "message" // "finish" // "message from callback" // "message from callback_1"
Цикл событий в JavaScript отличается от других языков тем, что его поток выполнения никогда не блокируется, кроме некоторых исключений, таких как alert или синхронный HTTP-запрос, которые не рекомендуется использовать. Поэтому даже когда приложение ожидает запросы из хранилища или ответ с сервера, оно может обрабатывать другие процессы, например пользовательский ввод.
Веб-сайты стали более интерактивными и динамичными, необходимость выполнения интенсивных операций стала все более актуальной (к примеру, выполнение внешних сетевых запросов для получения данных API). Чтобы обрабатывать эти операции, необходимо использование методов асинхронного программирования. Встроенный механизм Event Loop помогает JavaScript обрабатывать асинхронный код. Чтобы выйти на хороший уровень разработки с JavaScript потребуется не только ваше желание, но и наставничество практикующих специалистов. Наши друзья из школы Mate Academy с радостью помогут вам прокачать свои навыки и знания.
Прокси (proxy), или прокси-сервер — это программа-посредник, которая обеспечивает соединение между пользователем и интернет-ресурсом. Принцип…
Согласитесь, было бы неплохо соединить в одно сайт и приложение для смартфона. Если вы еще…
Повсеместное распространение смартфонов привело к огромному спросу на мобильные игры и приложения. Миллиарды пользователей гаджетов…
В перечне популярных чат-ботов с искусственным интеллектом Google Bard (Gemini) еще не пользуется такой популярностью…
Скрипт (англ. — сценарий), — это небольшая программа, как правило, для веб-интерфейса, выполняющая определенную задачу.…
Дедлайн (от англ. deadline — «крайний срок») — это конечная дата стачи проекта или задачи…