Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Promise.race() у JavaScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`Promise.race(iterable)` приймає масив (або будь-який ітерований об'єкт) промісів і повертає новий проміс, який завершується разом із першим промісом, що завершився, незалежно від того, успішно чи з помилкою. Це буквально «гонка»: хто завершився першим, той і визначає результат, а решта промісів продовжують працювати у фоні, але їхні результати ігноруються (проміси не скасовуються).** ```javascript const fast = new Promise(resolve => setTimeout(() => resolve('Швидкий'), 1000)); const slow = new Promise(resolve => setTimeout(() => resolve('Повільний'), 3000)); Promise.race([fast, slow]).then(console.log); // Швидкий ``` **Ключове:** найчастіше `Promise.race()` використовують для таймауту: гонка «корисна операція проти таймера» обриває операції, що зависли.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`Promise.race(iterable)` приймає масив (або будь-який ітерований об'єкт) промісів і повертає новий проміс, який виконується (`fulfilled`) або відхиляється (`rejected`) щойно завершився перший із переданих промісів.** Не має значення, успішно чи з помилкою: це «гонка» промісів, у якій хто завершився першим, той і визначає результат. ## Теорія ### TL;DR - `Promise.race(iterable)` повертає проміс, що повторює долю **першого завершеного** проміса. - Результат може бути як успіхом, так і помилкою: усе залежить від того, хто фінішував першим. - Решта промісів **ігноруються, але не скасовуються**, вони продовжують виконуватись у фоні. - Звичайні значення автоматично загортаються в `Promise.resolve()`, тому не проміс перемагає миттєво. - Головний практичний сценарій: таймаут для операції, що може зависнути. ### Швидкий приклад ```javascript const fast = new Promise(resolve => setTimeout(() => resolve('Швидкий'), 1000)); const slow = new Promise(resolve => setTimeout(() => resolve('Повільний'), 3000)); Promise.race([fast, slow]).then(console.log); ``` Результат через 1 секунду: ```javascript Швидкий ``` Другий проміс (`slow`) продовжить виконуватись у фоні, але результат уже визначений: «гонку» виграв `fast`. Синтаксис: ```javascript Promise.race(iterable) ``` Повертає новий `Promise`, який завершиться значенням першого успішно виконаного проміса (`resolve`) або впаде з помилкою, якщо першим завершиться `reject`. ### Як це працює під капотом 1. Усі проміси починають виконуватись *одночасно* (практично одночасно, бо кожен стартував у момент створення). 2. Щойно **один із них завершився** (`resolve` або `reject`): - результат цього проміса стає **результатом усієї гонки**; - `Promise.race()` **переходить у стан** `fulfilled` або `rejected`; - решта промісів **ігноруються** (але не скасовуються). 3. Решта промісів продовжують виконуватись у фоні, проте їхній результат ні на що не впливає, адже проміс змінює стан лише один раз. ### Якщо перший проміс завершився помилкою ```javascript const ok = new Promise(resolve => setTimeout(() => resolve('Все гаразд'), 2000)); const fail = new Promise((_, reject) => setTimeout(() => reject('Помилка!'), 1000)); Promise.race([ok, fail]) .then(console.log) .catch(console.error); ``` Результат через 1 секунду: ```javascript Помилка! ``` Навіть попри те, що `ok` згодом успішно виконається, результат усієї гонки вже вирішений помилкою. Саме цим `Promise.race()` відрізняється від `Promise.any()`, який ігнорує відхилення і чекає на перший успішний проміс. ### Таймаут на базі Promise.race() Поширена практика: використовувати `Promise.race()`, щоб поставити **обмеження за часом**. ```javascript function timeout(ms) { return new Promise((_, reject) => setTimeout(() => reject(`Перевищено час очікування (${ms} мс)`), ms) ); } const fetchData = new Promise(resolve => setTimeout(() => resolve('Відповідь отримано'), 3000) ); Promise.race([fetchData, timeout(2000)]) .then(console.log) .catch(console.error); ``` Вивід через 2 секунди: ```javascript Перевищено час очікування (2000 мс) ``` Це один із найчастіших сценаріїв застосування `Promise.race()`: обмеження операцій, що «зависають», наприклад мережевих запитів. ### Миттєві проміси та не проміси Якщо всі проміси завершуються миттєво, перемагає **перший у порядку ітерації**: ```javascript Promise.race([ Promise.resolve('Перший'), Promise.resolve('Другий'), Promise.resolve('Третій') ]).then(console.log); // Перший ``` Якщо передати не проміс, `Promise.race()` автоматично «загортає» звичайне значення у `Promise.resolve()`, тож воно виграє гонку одразу: ```javascript Promise.race([42, Promise.resolve('OK')]).then(console.log); // 42 ``` ### Відмінність від інших методів | Метод | Що робить | Завершується коли | При помилці | | --- | --- | --- | --- | | `Promise.all()` | Чекає на всі проміси | Усі завершені успішно | Падає на першому `reject` | | `Promise.allSettled()` | Чекає на всіх, незалежно від результату | Усі завершені | Не падає ніколи | | `Promise.race()` | Бере перший завершений | Перший `resolve` або `reject` | Залежить від першого проміса | | `Promise.any()` | Бере перший успішний (`resolve`) | Перший `fulfilled` | Падає, якщо **всі** `rejected` | Проста аналогія: > Уявіть гонку. У вас кілька кур'єрів: чий результат прийде першим, той ви і приймаєте. Решта ще в дорозі, але вам уже байдуже, потрібне ви отримали. Підсумок по методу: | Особливість | `Promise.race()` | | --- | --- | | Приймає | Масив (або інший ітерований об'єкт) промісів | | Завершується коли | Перший проміс виконається або відхилиться | | Повертає | Результат (або помилку) першого завершеного проміса | | Решта промісів | Ігноруються (але не скасовуються) | | Часте застосування | Обмеження за часом (`timeout`), конкуренція запитів | ### Типові помилки - **Думати, що програні проміси скасовуються.** Вони працюють далі: запит летить, таймер цокає, ресурси витрачаються. Для реального скасування потрібен `AbortController`. - **Плутати `race()` з `any()`.** `race()` завершиться помилкою, якщо першим завершився відхилений проміс, `any()` же чекає на перший успішний. - **Забувати `.catch()`.** Гонка з таймаутом за визначенням може відхилитись, і без обробника ви отримаєте `unhandled promise rejection`. - **Передавати порожній масив.** `Promise.race([])` ніколи не завершиться, бо переможцю просто нема звідки взятися. - **Лишати таймер після перемоги основної операції.** `setTimeout` із таймауту доживе до кінця і в Node.js триматиме процес живим, тому таймер варто чистити через `clearTimeout` у `finally`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.