Асинхронність
Увесь код виконується процесором вашого комп'ютера. Код, який ми писали досі, був синхронним, тобто займав процесор на увесь час свого виконання. Наприклад, швидкість виконання циклу для ітерації по масиву залежить від швидкості процесора.
Є операції, які взаємодіють із зовнішнім світом. Наприклад, обмін даними з сервером у мережі, що набагато повільніше, ніж отримання їх з пам'яті. Якщо такі операції обробляються синхронно, то процесор простоює під час мережевого запиту на сервер замість того, щоб виконувати інший код.
Синхронний код виконується послідовно, кожна інструкція очікує, доки виконається попередня. Коли ви викликаєте функцію, яка виконує тривалу дію, це зупиняє програму на увесь час її виконання. Тобто в моделі синхронного програмування все відбувається по черзі.
Уявіть чергу придбання квитків на потяг. Ви не можете почати купувати квиток доти, доки квиток не придбає людина перед вами. Точно так само люди, які стоять за вами, не можуть почати купувати квитки доти, доки ви не купите.
В асинхронному коді одночасно можуть виконуватися декілька операцій. У такій моделі мережевий запит на сервер не зупинить програму, вона продовжить виконувати інші операції. Коли запит завершиться, програма повідомляється про це і отримує доступ до результату (наприклад, даних з серверу).
Уявіть обід в ресторані. Ви та інші відвідувачі замовляєте їжу. Вам не потрібно чекати, доки їм принесуть їжу, перш ніж замовляти. Точно так само інші відвідувачі не повинні чекати, доки ви отримаєте свою страву і поїсте, перш ніж вони зможуть замовити. Кожен отримає свою страву, щойно її завершать готувати.
Розглянемо різницю на прикладі, в якому програма виконує два мережевих запити на
сервер, після чого обробляє їх результат. Операції 1
і 2
- це функції, які
роблять запити на сервер, а 3
, 4
і 5
- будь-який інший звичний для вас
код.
У синхронній моделі все зрозуміло і досить сумно - попередні операції блокують виконання наступних, доки вони не завершаться. Якщо операції 3-5 - це обробка кліків користувача, то інтерфейс просто зависне, доки не будуть виконані і оброблені результати запитів 1-2.
Наприклад, користувач відправив коментар (мережевий запит) і водночас захотів відкрити сайдбар з останніми новинами. Після кліка відправити коментар, інтерфейс зависне і не буде реагувати на його дії, доки від сервера не прийде результат відправлення коментаря. Погодьтеся, це не дуже зручно.
В асинхронній моделі старт мережевого запиту викликає щось на зразок розгалуження, тобто запуск запиту і результат його обробки - це різні дії. Доки виконується запит, програма продовжує працювати і виконувати інший код. Щойно мережевий запит буде виконаний, програма може почати обробляти його результат одразу після звільнення. Це означає, що користувач відправив коментар і відразу зміг відкрити сайдбар зі свіжими новинами, не чекаючи, доки прийде відповідь від сервера.
Тобто за одиницю часу, як і раніше, може виконуватися тільки одна операція, тому що JavaScript - однопотокова мова. Асинхронне програмування досягається шляхом відкладених викликів функцій, де ініціалізація асинхронної операції і обробка її результату - це різні дії.
Асинхронний код
У синхронному коді така інструкція не може почати своє виконання, доки не виконається попередня. Тобто інструкції обробляються послідовно.
console.log("First log");
console.log("Second log");
console.log("Third log");
Наступний код - асинхронний. З функцією setTimeout()
ми познайомимося далі.
Зараз про неї нам потрібно знати тільки те, що вона приймає два параметри:
callback-функцію, яка буде викликана після закінчення часу, який ми передаємо
другим аргументом.
// Will run first
console.log("First log");
setTimeout(() => {
// Will run last, after 2000 milliseconds
console.log("Second log");
}, 2000);
// Will run second
console.log("Third log");
Функція setTimeout()
відпрацьовує синхронно і реєструє відкладений виклик
переданої callback-функції, яка буде викликана асинхронно, через вказаний
проміжок часу.
Багатопотоковість
Не плутайте асинхронність і багатопотоковість (паралелізм) - це різні моделі програмування. Наведемо просту аналогію, яка все розставить на свої місця. Уявіть, що ви шеф у ресторані і надходить замовлення на каву і тости.
- Синхронний однопотоковий підхід - ви самі спочатку готуєте каву, потім тости і подаєте їх, після чого прибираєте на кухні.
- Асинхронний однопотоковий підхід - ви починаєте готувати каву і встановлюєте таймер, потім починаєте готувати тости і так само встановлюєте таймер. Доки кава і тости готуються, ви прибираєте на кухні. Коли таймери спрацьовують, ви знімаєте з вогню каву, дістаєте тости і подаєте їх.
- Багатопотоковий підхід (паралелізм) - ви наймаєте двох помічників, одного - для приготування кави, а іншого - для тостів. Тепер у вас є проблема управління помічниками (потоками), щоб вони не конфліктували один з одним на кухні під час спільного використання ресурсів.
В асинхронних однопотокових процесах у вас є графік завдань, в якому деякі завдання залежать від результату роботи інших. З виконанням кожного завдання викликається код для обробки його результату. Але вам потрібен тільки один працівник для виконання усіх завдань, а не один працівник для одного завдання.