Асинхронність у JavaScript
Асинхронність у JavaScript, це механізм, який дозволяє виконувати задачі без блокування основного потоку (main thread). Простіше кажучи, JavaScript може не чекати завершення довгих операцій (наприклад, запитів до сервера, читання файлів або таймерів), а продовжувати виконувати решту коду і обробити результат пізніше, коли він буде готовий.
Теорія
TL;DR
- JavaScript, це однопотокова мова: код виконується послідовно, по одній задачі за раз.
- Асинхронність потрібна, щоб не «заморожувати» сторінку під час довгих операцій.
- Асинхронність, це не паралельність, а механізм планування задач.
- Реалізована вона через Event Loop, стек викликів, Web API і чергу задач.
- Основні способи роботи: колбеки, проміси,
async/await. awaitзупиняє виконання всередині функції, але не блокує потік.
Швидкий приклад
console.log('1');
setTimeout(() => {
console.log('2'); // виконається пізніше
}, 1000);
console.log('3');Вивід:
1
3
2Так відбувається тому, що setTimeout, це асинхронна операція: вона «стає в чергу» і виконується після основного коду, через 1 секунду.
Синхронний код і блокування
console.log('1');
alert('Зачекайте...'); // блокує виконання
console.log('2');Поки користувач не закриє alert, програма зупинена, це синхронний код. Саме таку поведінку і усуває асинхронність: довга операція не тримає основний потік.
Як це працює під капотом
Асинхронність у JS реалізована через Event Loop (цикл подій).
- Основний код виконується у стеку викликів (Call Stack).
- Асинхронні операції (таймери,
fetch, події тощо) передаються у Web API (API браузера або Node.js). - Коли операція завершена, колбек потрапляє в чергу задач (Callback Queue).
- Event Loop перевіряє, чи вільний стек, і якщо так, бере задачу з черги та виконує її.
Таким чином, асинхронність, це не «паралельність», а механізм планування задач.
Основні способи роботи з асинхронністю
| Підхід | Приклад | Особливості |
|---|---|---|
| Callbacks (колбеки) | setTimeout(() => {...}, 1000) | Старий, незручний (callback hell) |
| Promises (проміси) | fetch(...).then(...) | Сучасніший, читабельніший |
| async / await | const data = await fetch(...) | Найзручніший синтаксис |
Приклад з Promise:
fetch('https://api.example.com/user')
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error(err));fetch повертає Promise, тобто обіцянку, що результат буде отриманий у майбутньому, і його можна обробити через .then().
Приклад з async/await:
async function loadUser() {
try {
const res = await fetch('https://api.example.com/user');
const user = await res.json();
console.log(user);
} catch (e) {
console.error(e);
}
}
loadUser();await «зупиняє» виконання всередині функції, доки проміс не виконається, але не блокує потік. Асинхронність зберігається.
Типові асинхронні операції в JS
setTimeout,setIntervalfetch()(мережеві запити)- Робота з файлами в Node.js (
fs.promises) - Події (
addEventListener) - WebSocket, IndexedDB, Web Workers тощо
Підсумок термінів
| Термін | Що означає |
|---|---|
| Асинхронність | Виконання задач «у фоні» без блокування основного потоку |
| Event Loop | Механізм, який керує чергою задач |
| Callback Queue | Черга готових до виконання задач |
| Promise | Об'єкт, що представляє результат асинхронної операції |
| async / await | Зручний синтаксис для роботи з промісами |
Типові помилки
- Плутати асинхронність із багатопотоковістю: другого потоку для вашого коду не з'являється, змінюється лише порядок виконання.
- Вважати, що
setTimeout(fn, 1000)гарантує рівно 1 секунду: це мінімальна затримка, задача чекатиме, доки звільниться стек. - Блокувати основний потік важким синхронним циклом і дивуватися, що асинхронні колбеки не спрацьовують вчасно.
- Забувати про обробку помилок: проміс без
.catch()абоtry...catchдає unhandled rejection. - Змішувати колбеки і проміси в одній ланцюжковій логіці замість того, щоб привести все до
async/await.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.