Як працює цикл подій у JavaScript?

Хоча для написання повномасштабного робочого коду може знадобитися глибоке розуміння таких мов, як C++ і C, JavaScript часто можна написати лише з базовим розумінням того, що можна робити з мовою.

Такі концепції, як передача зворотних викликів функціям або написання асинхронного коду, часто не так складно реалізувати, що змушує більшість розробників JavaScript менше піклуватися про те, що відбувається під капотом. Вони просто не дбають про розуміння складнощів, які глибоко абстрагувалися від них мовою.

Як розробнику JavaScript стає дедалі важливішим розуміти, що насправді відбувається під капотом і як насправді працює більшість цих складнощів, абстрагованих від нас. Це допомагає нам приймати більш обґрунтовані рішення, що, у свою чергу, може значно підвищити продуктивність нашого коду.

Ця стаття присвячена одному з дуже важливих, але рідко зрозумілих понять або термінів у JavaScript. ПЕТЛЯ ПОДІЙ!.

У JavaScript не можна уникнути написання асинхронного коду, але чому насправді означає асинхронний код? тобто цикл подій

Перш ніж ми зможемо зрозуміти, як працює цикл подій, ми спочатку повинні зрозуміти, що таке сам JavaScript і як він працює!

Що таке JavaScript?

Перш ніж ми продовжимо, я хотів би, щоб ми зробили крок назад до самих основ. Що насправді таке JavaScript? Ми могли б визначити JavaScript як;

JavaScript — це високорівнева, інтерпретована, однопотокова, неблокуюча, асинхронна, паралельна мова.

Почекай, що це? Книжкове визначення? 🤔

Давайте розберемося!

Ключові слова в цій статті: однопотоковий, неблокуючий, одночасний і асинхронний.

Одна нитка

Потік виконання — це найменша послідовність запрограмованих інструкцій, якою може незалежно керувати планувальник. Мова програмування є однопоточною, що означає, що вона може виконувати лише одне завдання або операцію одночасно. Це означає, що він виконає весь процес від початку до кінця без переривання або зупинки потоку.

На відміну від багатопоточних мов, де кілька процесів можуть виконуватися в кількох потоках одночасно, не блокуючи один одного.

Як JavaScript може бути однопоточним і неблокуючим одночасно?

Але що означає блокування?

Неблокування

Немає єдиного визначення блокування; це просто означає речі, які повільно виконуються в потоці. Отже, неблокування означає речі, які не сповільнюються в потоці.

Але зачекайте, я казав, що JavaScript працює в одному потоці? І я також сказав, що це неблокування, що означає, що завдання виконується швидко в стеку викликів? Але як??? А як щодо запуску таймерів? Петлі?

  Як написати прес-реліз [+ 4 Templates]

Розслабся! Ми дізнаємось трохи пізніше 😉.

Одночасний

Паралельність означає, що код виконується одночасно більш ніж одним потоком.

Гаразд, справи йдуть по-справжньому дивно Тепер, як JavaScript може бути однопоточним і одночасно працювати? тобто виконання коду з більш ніж одним потоком?

Асинхронний

Асинхронне програмування означає, що код виконується в циклі подій. Коли відбувається операція блокування, запускається подія. Код блокування продовжує працювати, не блокуючи основний потік виконання. Коли код блокування завершує роботу, він ставить у чергу результат операцій блокування та повертає їх назад у стек.

Але JavaScript має один потік? Що тоді виконує цей код блокування, дозволяючи іншим кодам у потоці виконуватися?

Перш ніж продовжити, давайте підсумуємо вищесказане.

  • JavaScript є однопоточним
  • JavaScript не блокує, тобто повільні процеси не блокують його виконання
  • JavaScript є паралельним, тобто виконує свій код у кількох потоках одночасно
  • JavaScript є асинхронним, тобто він запускає код блокування в іншому місці.

Але вищезазначене не зовсім відповідає, як однопотокова мова може бути неблокуючою, паралельною та асинхронною?

Давайте підемо трохи глибше, давайте перейдемо до механізмів виконання JavaScript, V8, можливо, він має деякі приховані потоки, про які ми не знаємо.

Двигун V8

Механізм V8 — це високопродуктивний механізм виконання веб-складання з відкритим кодом для JavaScript, написаний Google на C++. Більшість браузерів запускають JavaScript за допомогою двигуна V8, і навіть популярне середовище виконання node js також використовує його.

Простою англійською мовою V8 — це програма на C++, яка отримує код JavaScript, компілює та виконує його.

V8 робить дві основні речі;

  • Розподіл пам’яті купи
  • Контекст виконання стека викликів

На жаль, наші підозри були помилковими. V8 має лише один стек викликів, уявіть стек викликів як потік.

Один потік === один стек викликів === одне виконання за раз.

Зображення – Hacker Noon

Оскільки V8 має лише один стек викликів, як тоді JavaScript працює одночасно й асинхронно, не блокуючи основний потік виконання?

  Як змінити регіон Netflix і дивитися будь-яку країну Netflix (на кожному пристрої)

Давайте спробуємо це з’ясувати, написавши простий, але поширений асинхронний код і проаналізуємо його разом.

JavaScript виконує кожен код рядок за рядком, один за одним (однопотоковий). Як і очікувалося, тут на консолі друкується перший рядок, але чому останній рядок друкується перед кодом очікування? Чому процес виконання не чекає коду очікування (блокування) перед тим, як виконати останній рядок?

Якийсь інший потік, здається, допоміг нам виконати цей тайм-аут, оскільки ми майже впевнені, що потік може виконати лише одне завдання в будь-який момент часу.

Давайте коротко заглянемо в Вихідний код V8 на деякий час.

Чекати, що??!!! У V8 немає функцій таймера, немає DOM? Немає подій? Немає AJAX?… Еееесссс!!!

Події, DOM, таймери тощо не є частиною основної реалізації JavaScript, JavaScript суворо відповідає специфікаціям сценаріїв Ecma, і різні його версії часто називаються відповідно до специфікацій сценаріїв Ecma (ES X).

Робочий процес виконання

Події, таймери, запити Ajax надаються браузерами на стороні клієнта і часто називаються веб-API. Саме вони дозволяють однопоточному JavaScript бути неблокуючим, одночасним і асинхронним! Але як?

У робочому процесі виконання будь-якої програми JavaScript є три основні розділи: стек викликів, веб-API і черга завдань.

Стек викликів

Стек — це структура даних, у якій останній доданий елемент завжди видаляється першим зі стеку. Ви можете уявити це як стек пластини, у якій лише перша пластина, яка була додана останньою, може бути видалена першою. Стек викликів — це просто структура даних стека, де відповідно виконуються завдання або код.

Давайте розглянемо наведений нижче приклад;

Джерело – https://youtu.be/8aGhZQkoFbQ

Коли ви викликаєте функцію printSquare() , вона надсилається в стек викликів, функція printSquare() викликає функцію square(). Функція square() поміщається в стек і також викликає функцію multiply(). Функція множення поміщається в стек. Оскільки функція множення повертається і є останньою функцією, яку було передано в стек, вона спочатку вирішується та видаляється зі стеку, потім функція square(), а потім функція printSquare().

Веб API

Тут виконується код, який не обробляється механізмом V8, щоб не «блокувати» основний потік виконання. Коли стек викликів стикається з функцією веб-API, процес негайно передається до веб-інтерфейсу API, де він виконується, звільняючи стек викликів для виконання інших операцій під час його виконання.

  Яка різниця між 5G та 5GHz Wi-Fi?

Давайте повернемося до нашого прикладу setTimeout вище;

Коли ми запускаємо код, перший рядок console.log надсилається до стеку, і ми отримуємо результати майже одразу, коли досягає тайм-ауту, таймери обробляються браузером і не є частиною основної реалізації V8, вони надсилаються замість цього до веб-API, звільняючи стек, щоб він міг виконувати інші операції.

Поки тайм-аут ще триває, стек переходить до наступного рядка дій і запускає останній console.log, що пояснює, чому ми отримуємо цей вивід перед виведенням таймера. Коли таймер закінчується, щось відбувається. Таймер console.log in then чарівним чином знову з’являється в стеку викликів!

як?

Цикл подій

Перш ніж ми обговоримо цикл подій, давайте спочатку пройдемося по функції черги завдань.

Повернемося до нашого прикладу тайм-ауту. Щойно веб-API завершує виконання завдання, він не просто автоматично повертає його назад у стек викликів. Він переходить до черги завдань.

Черга — це структура даних, яка працює за принципом «першим прийшов, першим вийшов», тож коли завдання надходять у чергу, вони виходять у тому самому порядку. Завдання, виконані веб-інтерфейсами API, які надсилаються до черги завдань, а потім повертаються до стека викликів, щоб отримати роздрукований результат.

Але почекай. ЩО ТАКЕ ЦИКЛ ПОДІЙ???

Джерело – https://youtu.be/8aGhZQkoFbQ

Цикл подій — це процес, який очікує, поки стек викликів буде очищено, перш ніж надсилати зворотні виклики з черги завдань у стек викликів. Коли стек очищено, запускається цикл подій і перевіряє чергу завдань на наявність доступних зворотних викликів. Якщо такі є, він надсилає його до стеку викликів, чекає, поки стек викликів знову очиститься, і повторює той самий процес.

Джерело – https://www.quora.com/How-does-an-event-loop-work/answer/Timothy-Maxwell

Наведена вище схема демонструє базовий робочий процес між циклом подій і чергою завдань.

Висновок

Хоча це дуже простий вступ, концепція асинхронного програмування в JavaScript дає достатньо розуміння, щоб чітко зрозуміти, що відбувається під капотом і як JavaScript може працювати одночасно та асинхронно лише з одним потоком.

JavaScript завжди доступний на вимогу, і якщо вам цікаво дізнатися, я б порадив вам перевірити це Курс Udemy.