Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що ES6 дав для асинхронності». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Головне, що ES6 дав для асинхронності, це `Promise`: об'єкт, який представляє результат асинхронної операції, що може прийти зараз, пізніше або ніколи.** Разом із ним прийшли `.then()` для успішного результату, `.catch()` для помилок, ланцюжки промісів замість вкладених колбеків, а також `Promise.resolve`, `Promise.reject`, `Promise.all` і `Promise.race`. Саме це стало фундаментом, поверх якого в ES2017 з'явився `async/await`. ```javascript fetch('/api/data') .then(res => res.json()) .then(data => process(data)) .catch(error => console.error(error)); ``` **Ключове:** ES6 замінив callback hell на керовану модель проміс, а `async/await` це вже синтаксичний цукор над нею.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Головне, що ES6 (2015) приніс для асинхронності, це `Promise`: об'єкт, який представляє результат асинхронної операції, що може бути отриманий зараз, пізніше або ніколи.** Разом із ним з'явилися `.then()`, `.catch()`, ланцюжки викликів і статичні методи `Promise.all` та `Promise.race`, які прибрали з коду вкладені колбеки. ## Теорія ### TL;DR - До ES6 асинхронність будували на колбеках, і глибока вкладеність давала callback hell. - ES6 увів `Promise`: «обіцянку» повернути результат, коли операція завершиться. - `.then()` обробляє успіх, `.catch()` централізовано ловить помилки. - Ланцюжки `.then()` роблять код лінійним: результат одного кроку йде в наступний. - ES6 також дав `Promise.resolve`, `Promise.reject`, `Promise.all` і `Promise.race`. - `async/await` це вже ES2017, синтаксичний цукор поверх промісів, а не заміна їм. ### Швидкий приклад ```javascript const promise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Дані отримано'); }, 1000); }); promise.then(result => { console.log(result); // Дані отримано }); ``` Що тут відбувається: - `new Promise()` створює обіцянку. - Колбек отримує два аргументи: `resolve()` позначає успішне завершення, `reject()` позначає помилку. - Метод `.then()` викликається при успішному результаті. - Метод `.catch()` ловить помилки. ### До ES6: callback hell До 2015 року основний спосіб працювати з асинхронним кодом виглядав приблизно так: ```javascript getData(function (res) { process(res, function (result) { save(result, function (response) { console.log('Done!', response); }); }); }); ``` Читати й налагоджувати такий код важко: помилки треба ловити вручну на кожному рівні, а логіка тоне у вкладеності. Саме цю проблему й називають callback hell. ### Promise: основа, яку дав ES6 Проміс описує «обіцянку» повернути результат, коли операція завершиться, і має три стани: `pending` (очікує), `fulfilled` (виконано) і `rejected` (відхилено). Перехід зі стану `pending` відбувається рівно один раз і назад не повертається. Обробка помилок стає передбачуваною: ```javascript const promise = new Promise((resolve, reject) => { const success = false; if (success) resolve('OK'); else reject('Помилка'); }); promise .then(res => console.log('Успішно:', res)) .catch(err => console.log('Помилка:', err)); ``` ### Ланцюжки промісів Кілька `.then()` можна пов'язати між собою: результат одного кроку передається в наступний. ```javascript fetch('/api/data') .then(res => res.json()) .then(data => process(data)) .then(result => save(result)) .catch(error => console.error(error)); ``` Код став лінійним і читабельним, на відміну від вкладених колбеків. Один `.catch()` у кінці перехоплює помилку з будь-якого кроку ланцюжка. ### Статичні методи Promise | Метод | Що робить | | --- | --- | | `Promise.resolve(value)` | Створює вже успішно завершений проміс | | `Promise.reject(error)` | Створює відхилений проміс | | `Promise.all([p1, p2, ...])` | Запускає проміси паралельно і чекає завершення всіх | | `Promise.race([p1, p2, ...])` | Повертає результат першого завершеного промісу | Приклад `Promise.all`: ```javascript Promise.all([ fetch('/user'), fetch('/posts'), fetch('/comments') ]) .then(([userRes, postsRes, commentsRes]) => { console.log('Усі дані отримано!'); }) .catch(err => console.error('Помилка:', err)); ``` > Якщо хоча б один проміс буде відхилено, увесь `Promise.all()` завершиться помилкою. ### Що з'явилося пізніше Питання саме про ES6, але важливо бачити всю еволюцію: | Версія | Нове для асинхронності | | --- | --- | | **ES6 (2015)** | `Promise`, `.then()`, `.catch()`, `Promise.all()`, `Promise.race()` | | **ES2017** | `async/await`, синтаксичний цукор над промісами | | **ES2020 і пізніше** | `Promise.allSettled()`, `Promise.any()`, `Promise.withResolvers()` | Отже, ES6 став фундаментом, а наступні версії лише покращили й спростили роботу з ним. Чому це важливо: | До ES6 | Після ES6 | | --- | --- | | Колбеки і callback hell | Проміси та читабельна структура | | Помилки ловляться вручну на кожному рівні | `.catch()` для централізованої обробки | | Багато вкладеності | Ланцюжки `.then()` | | Немає гарантій порядку виконання | Керована, передбачувана асинхронність | Коротко: до ES6 асинхронність трималася на колбеках, ES6 дав проміси як керовану модель, а ES2017 додав `async/await` поверх них. ### Типові помилки 1. **Не повертати проміс усередині `.then()`.** Без `return` наступний крок отримає `undefined` і не дочекається операції: ```javascript fetch('/api/data') .then(res => { res.json(); }) // немає return .then(data => console.log(data)); // undefined ``` 2. **Створювати проміс навколо того, що вже є промісом.** `new Promise(resolve => fetch(url).then(resolve))` це зайва обгортка, яка до того ж губить помилки; достатньо повернути сам `fetch(url)`. 3. **Забувати `.catch()`.** Необроблене відхилення дає `unhandled promise rejection`, а в Node.js може завершити процес. 4. **Вважати, що `Promise.all` запускає операції.** Проміси стартують у момент створення, тому `Promise.all` лише чекає на них; послідовне створення в циклі з `await` виконає їх один за одним, а не паралельно. 5. **Брати `Promise.all` там, де потрібен `Promise.allSettled`.** Перший падає на першій же помилці й губить успішні результати; коли треба дізнатися долю кожної операції, потрібен `allSettled`. 6. **Плутати проміс із синхронним значенням.** `.then()` завжди виконується асинхронно, у черзі мікрозадач, тож код одразу після виклику відпрацює раніше за обробник.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.