Event Loop - это одна из тех вещей, с которыми так или иначе сталкивается каждый разработчик JavaScript, но поначалу немного сложно понять что к чему.
JavaScript - это однопоточный язык: одновременно может выполняться только одна задача. Обычно в этом нет ничего сложного, но теперь представьте, что вы запускаете задачу, которая занимает 30 секунд... Да. Во время этой задачи мы ждем 30 секунд, прежде чем что-либо еще может произойти (по умолчанию JavaScript запускается в главном потоке браузера, поэтому весь пользовательский интерфейс будет ждать).
К счастью, браузер предоставляет нам некоторые функции, которые сам механизм JavaScript не предоставляет: Web API. Который включает в себя DOM API, setTimeout, HTTP-запросы и так далее. Это может помочь нам создать асинхронное неблокирующее поведение.
Когда мы вызываем функцию, она добавляется в call stack (стек вызовов). Стек вызовов является частью механизма JS, это не зависит от браузера. Это классический взгляд на стек, т.е first in - last out. Когда функция возвращает значение, она "выталкивается" из стека.
Функция respond возвращает функцию setTimeout. SetTimeout предоставляется нам через веб-API: он позволяет нам делать задачи, не блокируя основной поток. Callback функция, которую мы передали в функцию setTimeout, лямбда функция () => {return 'Hey'} добавляется в веб-API. Тем временем функция setTimeout и функция respond извлекаются из стека, они оба возвращают свои значения.
В Web API таймер работает до тех пор, пока второй аргумент, который мы передали ему, не подождет 1000 мс. Callback не сразу добавляется в стек вызовов, а передается в нечто, называемое очередью.
Это может сбивать с толку: это не означает, что callback функия добавляется в стек вызовов (таким образом, возвращает значение) через 1000 мс! Он просто добавляется в очередь через 1000 мс. Но в этой очереди, функция должна ждать пока придет ее черёд.
Теперь это та часть, которую мы все ждали... Время для Event Loop выполнить единственную задачу: соединить очередь со стеком вызовов! Если стек вызовов пуст, то есть, если все ранее вызванные функции вернули свои значения и были извлечены из стека, первый элемент в очереди добавляется в стек вызовов. В этом случае никакие другие функции не были вызваны, что означает, что стек вызовов был пуст к тому времени, когда callback функция была первым элементом в очереди.
Callback добавляется в стек вызовов, вызывается и возвращает значение, а также извлекается из стека.
Попробуйте выяснить, что появится в консоли, если мы запустим следующее:
const foo = () => console.log("First") const bar = () => setTimeout(() => console.log("Second"), 500) const baz = () => console.log("Third") bar() foo() baz()
Давайте посмотрим, что происходит, когда мы запускаем этот код в браузере: