Перейти к основному содержимому

Асинхронность Event loop

@serverSerrverlesskiy

Асинхронность

В JavaScript асинхронность — основной инструмент, который обрабатывает запросы параллельно с загрузкой веб-страницы. Сейчас невозможно представить интернет, где все запросы на сервер отправлялись бы с перезагрузкой страницы.

Любые данные от сервера запрашиваются асинхронно: отправляется запрос (XMLHttpRequest или XHR), и код не ждёт его возвращения, продолжая выполняться. Когда же сервер отвечает, объект XHR получает уведомление об этом и запускает функцию⚙️ обратного вызова — callback, который передали в него перед отправкой запроса.

Если правильно использовать инструменты языка , то выполнение запроса, который происходит последовательно и в одном потоке, никак не мешает приёму событий и реакции на них — человек спокойно работает с интерфейсом, не замечая лагов, сбоев и зависаний.

Видео

Event loop

Queue

Event loop в JavaScript — менеджер асинхронных вызовов.

Чтобы этот хитрый процесс слаженно работал, в JavaScript реализован механизм для управления очерёдностью исполнения кода . Поскольку это однопоточный язык , возникла необходимость "вклиниваться" в текущий контекст исполнения. Этот механизм называется event loop — событийный цикл.

С английского loop переводится как "петля", что отлично отражает смысл: мы имеем дело с закольцованной очередью.

Event loop регулирует последовательность исполнения контекстов — стек. Он формируется, когда сработало событие или была вызвана функция⚙️. Реакция на событие помещается в очередь исполнения, в event loop, который последовательно, с каждым циклом выполняет попадающий в него код . При этом привязанная к событию функция⚙️ вызывается следующей после текущего контекста исполнения.

В JavaScript постоянно работают связанные между собой синхронная и асинхронная очереди выполнения. Синхронная — stack — формирует очередь и пробрасывает в асинхронную — event loop — вызовы функций⚙️, которые будут выполнены после текущего запланированного исполняемого контекста.

Чтобы данные находились в консистентном состоянии, каждая функция⚙️ должна быть выполнена до конца. Это обусловлено однопоточностью JavaScript и некоторыми другими особенностями, например характерными для функциональных ⚙️языков программирования замыканиями. Поэтому единственный поток представлен в виде очереди контекстов исполнения, в которой и происходит "вклинивание" функций⚙️, прошедших через цикл событий.

Описание

JavaScript это однопоточный язык: одновременно может выполняться только одна задача. Обычно в этом нет ничего сложного, но теперь представьте, что вы запускаете задачу, которая занимает 30 секунд... Да. Во время этой задачи мы ждем 30 секунд, прежде чем что-либо еще может произойти (по умолчанию JavaScript запускается в главном потоке браузера, поэтому весь пользовательский интерфейс будет ждать) Сейчас 2021 год, никто не хочет медленный сайт который тупит.

К счастью, браузер предоставляет нам некоторые функции, которые сам механизм JavaScript не предоставляет: Web API. Который включает в себя DOM API, setTimeout, HTTP-запросы и так далее. Это может помочь нам создать асинхронное неблокирующее поведение .

Когда мы вызываем функцию, она добавляется в call stack(стек вызовов). Стек вызовов является частью механизма JS, это не зависит от браузера. Это классический взгляд на стек, т.е first in, last out. Когда функция возвращает значение, она "выталкивается" из стека.

function great() {
return 'Hello'
}

function respond() {
return setTimeout(() => alert('Hey!'), 1000)
}

great()
respond()

stack

Функция respond возвращает функцию setTimeout. SetTimeout предоставляется нам через Web-API: он позволяет нам делить задачи, не блокируя основной поток. Callback функция, которую мы передали в функцию setTimeout, лямбда функция () => {return 'Hey'} добавляется в Web-API. Тем временем setTimeout и responde извлекаются из стека и возвращают свои значения.

timer

В Web-API таймер работает до тех пор, пока второй аргумент, который мы передали ему, не подождет 1000 мс. Callback не сразу добавляется в стек вызовов, а передается в нечто, называемое очередью.

queue

Это может сбивать с толку: это не означает, что callback функция добавляется в стек вызовов (таким образом, возвращает значение) через 1000 мс! Он просто добавляется в очередь через 1000 мс. Но в этой очереди, функция должна ждать пока придет ее черёд.

Теперь это та часть, которую мы все ждали... Время для event loop выполнить единственную задачу: соединить очередь со стеком вызовов! Если стек вызовов пуст, то есть, если все ранее вызванные функции вернули свои значения и были извлечены из стека, первый элемент в очереди добавляется в стек вызовов. В этом случае никакие другие функции не были вызваны, что означает, что стек вызовов был пуст к тому времени, когда callback функция была первым элементом в очереди.

qtoc

callback добавляется в стек вызовов, вызывается и возвращает значение, а также извлекается из стека.

result

Смотреть весело, но вы не сможете полностью понять тему, не работая с ней снова и снова. Попробуйте выяснить, что появится в консоли, если мы запустим следующее:

const foo = () => console.log('First')
const bar = () => setTimeout(() => console.log('Second'), 500)
const baz = () => console.log('Third')

bar()
foo()
baz()

Давайте посмотрим, что происходит, когда мы запускаем этот код в браузере:

br

Мы вызываем bar, которая возвращает функцию setTimeout. Callback который мы передали в setTimeout добавляется в Web API, функция setTimeout и bar извлекаются из стека вызовов.

Таймер запускается, тем временем foo вызывается и записывает в журнал First. foo возвращает undefined, baz вызывается и callback добавляется в очередь baz логирует Third. Цикл обработки событий видит, что коллстек пуст после возврата baz, после чего колбэк добавляется в стек вызовов. Callback логирует Second.

Надеюсь, что это заставит вас чувствовать себя более уверено с циклом событий event loop!

Не беспокойтесь, если это все еще кажется запутанным, самое важное - понять, откуда могут возникнуть определенные ошибки или специфическое поведение.

EnglishMoji!

Проблемы?

Problem

Пишите в Discord или телеграмм чат, а также подписывайтесь на наши новости

JavaScript Camp

Вопросы:

Question

Асинхронность - это:

  1. Инструмент, который выводит контекст исполнения функции из синхронного потока
  2. Инструмент, который исполняет код построчно
  3. Инструмент, который обрабатывает запросы параллельно с загрузкой веб-страниц

Менеджер асинхронных вызовов:

  1. stack
  2. Event loop
  3. Объекты высшего класса

Инструмент, выполняющий код с задержкой в миллисекундах:

  1. delay
  2. heap
  3. setTimeout

Для того чтобы понять, на сколько вы усвоили этот урок, пройдите тест в мобильном приложении нашей школы по этой теме или в нашем телеграм боте.

EnglishMoji!

Ссылки:

  1. Объяснение работы EventLoop в JavaScript
  2. Как управлять event loop в JavaScript
  3. Справочник javascript
  4. Статья: Объяснение Event Loop в Javascript с помощью визуализации
  5. Статья: JavaScript Visualized: Promises & Async/Await

Contributors ✨

Thanks goes to these wonderful people (emoji key):


AlisaNasibullina


Dmitriy Vasilev

💵

Resoner2005

🐛 🎨