Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Ланцюжок промісів». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Ланцюжок промісів це послідовне виконання асинхронних кроків, у якому кожен `.then()`, `.catch()` і `.finally()` повертає новий проміс, тому результат одного кроку стає вхідними даними наступного.** Якщо з `.then()` повернути звичайне значення, воно загортається в `Promise.resolve(value)` і летить далі; якщо повернути проміс, ланцюжок чекає на його завершення; якщо кинути помилку, усі наступні `.then()` пропускаються і керування стрибає в найближчий `.catch()`. Після `.catch()` ланцюжок продовжується як успішний, а `.finally()` спрацьовує в будь-якому разі. ```javascript Promise.resolve(10) .then(n => n * 2) // 20 .then(n => n + 5) // 25 .then(n => console.log(n)) // 25 .catch(err => console.error(err)); ``` **Ключове:** кожен крок повертає новий проміс, значення йдуть вниз ланцюжком, а помилки летять вниз до першого `.catch()`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Ланцюжок промісів це послідовне виконання асинхронних дій, де результат одного `.then()` передається наступному.** Кожен `.then()`, `.catch()` і `.finally()` повертає новий проміс, і саме це дозволяє вибудувати лінійний потік обробки, без «callback hell». ## Теорія ### TL;DR - Кожен `.then()` повертає **новий проміс**, тому виклики складаються в ланцюжок. - Значення, повернуте з `.then()`, стає аргументом наступного `.then()`. - Якщо з `.then()` повернути проміс, ланцюжок чекає на його завершення. - Помилка в будь-якому кроці пропускає всі наступні `.then()` і йде в найближчий `.catch()`. - Після `.catch()` ланцюжок продовжується, бо `.catch()` теж повертає новий проміс. - `.finally()` виконується завжди і не змінює значення, якщо сам не кидає помилку. ### Швидкий приклад ```javascript new Promise((resolve) => { setTimeout(() => resolve(10), 1000); }) .then((num) => { console.log('Step 1, received:', num); return num * 2; }) .then((num) => { console.log('Step 2, multiplied:', num); return num + 5; }) .then((result) => { console.log('Step 3, total:', result); }); ``` Вивід: ```text Step 1, received: 10 Step 2, multiplied: 20 Step 3, total: 25 ``` Кожен `.then()`: 1. отримує результат попереднього кроку, 2. повертає **новий проміс**, 3. передає його результат далі по ланцюжку. ### Як це працює під капотом 1. **Перший проміс** створюється і переходить у `pending`. Коли викликано `resolve(value)`, він стає `fulfilled`. 2. Перший `.then()` отримує `value` і повертає **новий проміс**, який тепер чекає на наступний результат. 3. Кожен `.then()` у ланцюжку **дочекується виконання попереднього**, навіть якщо всередині повертається новий проміс. Приклад з асинхронним кроком: ```javascript Promise.resolve(1) .then((n) => { console.log('A:', n); return new Promise((resolve) => setTimeout(() => resolve(n + 1), 1000) ); }) .then((n) => { console.log('B:', n); return n * 3; }) .then((n) => console.log('C:', n)); ``` Вивід, через секунду: ```text A: 1 B: 2 C: 6 ``` Тут `.then()` «розпізнає», що йому повернули новий проміс, чекає, поки той завершиться (`resolve`), і лише потім виконує наступний крок ланцюжка. ### Що можна повертати з then() | Що повертає `.then()` | Що станеться | | --- | --- | | **Звичайне значення** | Загортається в `Promise.resolve(value)` | | **Проміс** | Ланцюжок чекає, поки він завершиться | | **Помилка (`throw`)** | Ланцюжок переходить у `rejected` | | **Нічого не повертає** | Передає `undefined` далі | Приклад комбінування значень і промісів: ```javascript Promise.resolve('A') .then((v) => v + 'B') // returns 'AB' .then((v) => Promise.resolve(v + 'C')) // waits for another promise .then((v) => v + 'D') .then(console.log); // ABCD ``` ### Помилки в ланцюжку Якщо в будь-якому `.then()` станеться помилка (`throw` або `reject`), усі наступні `.then()` **пропускаються**, і керування переходить у **найближчий** `.catch()`. ```javascript Promise.resolve(10) .then((x) => x / 2) .then(() => { throw new Error('Failure!'); }) .then(() => console.log('this will not run')) .catch((err) => console.error('Caught:', err.message)) .then(() => console.log('carrying on')); ``` Вивід: ```text Caught: Failure! carrying on ``` `.catch()` можна ставити і посеред ланцюжка, щоб відновитися після помилки: ```javascript Promise.reject('Failure') .catch((err) => { console.log('Caught:', err); return 'Recovered'; }) .then((v) => console.log('Next:', v)); ``` Вивід: ```text Caught: Failure Next: Recovered ``` `.catch()` теж повертає новий проміс, тому після обробки помилки ланцюжок продовжується. ### finally() у ланцюжку `finally()` виконується **завжди**, незалежно від результату, і не змінює значення, якщо не кидає помилку: ```javascript Promise.resolve('OK') .finally(() => console.log('Cleaning up...')) .then(console.log); ``` Вивід: ```text Cleaning up... OK ``` Повний ланцюжок з помилкою, відновленням і завершальним кроком: ```javascript Promise.resolve(1) .then((x) => x + 1) .then(() => { throw new Error('Oops!'); }) .catch((e) => { console.log('Error caught:', e.message); return 5; }) .then((x) => console.log('Result:', x)) .finally(() => console.log('Done')); ``` Вивід: ```text Error caught: Oops! Result: 5 Done ``` Аналогія: ланцюжок промісів це конвеєр. Кожен `.then()` це станція, де деталь обробляють; якщо щось пішло не так, `.catch()` лагодить помилку і повертає деталь на конвеєр; наприкінці `.finally()` вимикає верстати і підмітає підлогу. ### Підсумок | Особливість | Опис | | --- | --- | | Кожен `.then()` | Повертає новий проміс | | Значення йдуть вниз | Результат із `.then()` іде в наступний | | Помилки | Летять вниз до найближчого `.catch()` | | `.finally()` | Виконується в будь-якому разі | | Ланцюжок | Дозволяє писати асинхронний код лінійно, без вкладеності | ### Типові помилки - **Забути `return` у `.then()`.** Без нього наступний крок отримає `undefined`, а ланцюжок не чекатиме вкладений проміс. - **Вкладати `.then()` один в одний.** Це відтворює callback hell, який ланцюжок мав прибрати; повертайте проміс замість вкладення. - **Ставити `.catch()` лише на початку.** Він ловить те, що сталося вище за нього, помилки з наступних кроків пройдуть повз. - **Думати, що `.catch()` перериває ланцюжок.** Після нього проміс знову успішний, і наступні `.then()` виконаються. - **Вважати, що синхронний код у `.then()` виконається одразу.** Обробники завжди запускаються як мікрозадачі, після поточного синхронного коду. - **Губити помилки без фінального `.catch()`.** Ланцюжок без нього дає unhandled rejection, а в Node.js це може завершити процес.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.