Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Promise.any() у JavaScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`Promise.any(iterable)` приймає масив (або будь-який ітерований об'єкт) промісів і повертає новий проміс, який виконується значенням першого проміса, що завершився успішно, а всі відхилення дорогою просто ігнорує. Якщо ж усі проміси відхилилися, він відхиляється спеціальною помилкою `AggregateError`, у якої в полі `errors` лежить масив усіх причин у порядку входу.** ```javascript Promise.any([Promise.reject('Помилка 1'), Promise.resolve('Успіх')]) .then(console.log) // Успіх .catch(err => console.error(err.errors)); ``` **Ключове:** `any()` це «дай перший успішний результат», на відміну від `race()`, який реагує на перший завершений проміс, навіть якщо той відхилений.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`Promise.any(iterable)` приймає масив (або будь-який ітерований об'єкт) промісів і повертає новий проміс, який виконується (`fulfilled`) щойно перший проміс завершився успішно (`resolve`).** Відхиляється він лише тоді, коли відхилені всі проміси, тобто логіка звучить так: «дай мені перший успішний результат, а помилки ігноруй». ## Теорія ### TL;DR - `Promise.any(iterable)` віддає значення **першого успішного** проміса. - Проміжні відхилення ігноруються, вони не зупиняють очікування. - Якщо відхилились **усі**, повернений проміс падає з `AggregateError`. - `err.errors` містить масив усіх причин відхилення. - Не проміси загортаються у `Promise.resolve()`, тому звичайне значення перемагає одразу. - Класичний сценарій: fallback до кількох джерел даних або надлишкові запити до API. ### Швидкий приклад ```javascript const p1 = Promise.reject('Помилка 1'); const p2 = new Promise(resolve => setTimeout(() => resolve('Успіх 2'), 1000)); const p3 = new Promise(resolve => setTimeout(() => resolve('Успіх 3'), 2000)); Promise.any([p1, p2, p3]) .then(result => console.log('Результат:', result)) .catch(err => console.error('Помилка:', err)); ``` Вивід через 1 секунду: ```javascript Результат: Успіх 2 ``` Перший успішний (`p2`) «перемагає», навіть якщо решта ще виконується або впаде пізніше. Синтаксис: ```javascript Promise.any(iterable) ``` Повертає новий `Promise`, який завершиться значенням **першого `fulfilled` проміса** або відхилиться спеціальною помилкою `AggregateError`, якщо всі проміси впали. ### Як це працює під капотом 1. Усі проміси запускаються **паралельно**, точніше кожен уже виконується з моменту створення. 2. Якщо хоча б один із них **виконався успішно** (`resolve`), `Promise.any()` **негайно завершується з цим результатом**. 3. Якщо **всі** виявились `rejected`, повертається **помилка типу `AggregateError`** з масивом усіх причин. Тобто відхилення не завершує очікування: лічильник помилок просто зростає, і тільки коли він зрівняється з кількістю промісів, спрацює `catch`. ### Якщо всі проміси відхиляються ```javascript const p1 = Promise.reject('Помилка A'); const p2 = Promise.reject('Помилка B'); Promise.any([p1, p2]) .then(console.log) .catch(err => console.error(err)); ``` Вивід: ```javascript AggregateError: All promises were rejected ``` Помилка містить **список усіх причин**: ```javascript err.errors // ['Помилка A', 'Помилка B'] ``` Детальніше про поведінку `AggregateError`: ```javascript Promise.any([ Promise.reject('Помилка 1'), Promise.reject('Помилка 2') ]).catch(err => { console.error(err.name); // AggregateError console.error(err.errors); // ['Помилка 1', 'Помилка 2'] }); ``` Порядок в `err.errors` відповідає порядку вхідних промісів, а не порядку, у якому вони відхилились. ### Комбінація успіхів і помилок ```javascript const slow = new Promise(resolve => setTimeout(() => resolve('Повільний успіх'), 2000)); const fail = Promise.reject('Помилка!'); const fast = new Promise(resolve => setTimeout(() => resolve('Швидкий успіх'), 500)); Promise.any([slow, fail, fast]) .then(console.log) .catch(console.error); ``` Результат через 0.5 секунди: ```javascript Швидкий успіх ``` Навіть якщо `fail` упав раніше за всіх, `Promise.any()` чекає на перший **успішний** проміс. ### Fallback до кількох джерел Уявіть, що у вас три сервери, і треба отримати дані **з першого, який відповість успішно**, навіть якщо два інші недоступні: ```javascript const fetchFromA = fetch('/api/a'); // може впасти const fetchFromB = fetch('/api/b'); const fetchFromC = fetch('/api/c'); Promise.any([fetchFromA, fetchFromB, fetchFromC]) .then(res => console.log('Відповідь отримано')) .catch(() => console.error('Усі джерела недоступні')); ``` Так реалізується стратегія fallback: **успіх будь-якого джерела вважається достатнім**. ### Відмінність від інших методів | Метод | Що повертає | Завершується коли | Помилка при | | --- | --- | --- | --- | | `Promise.all()` | Масив результатів усіх успішних | Усі проміси `fulfilled` | Перший `reject` | | `Promise.allSettled()` | Масив статусів (`fulfilled` / `rejected`) | Усі проміси завершені | Ніколи | | `Promise.race()` | Перший завершений проміс (успіх або помилка) | Будь-який `resolve` чи `reject` | Завершується на першому `reject` | | `Promise.any()` | Перший успішний результат | Перший `fulfilled` | Коли **всі** `rejected` | Тобто: - `Promise.race()` реагує на **перший завершений** проміс, включно з помилками; - `Promise.any()` реагує лише на **перший успішний**. Проста аналогія: > Уявіть, що ви відправили трьох кур'єрів різними маршрутами. > > - `Promise.any()` каже: «чий результат **першим успішно дійде**, той я і прийму, а помилки решти мене не цікавлять». > - І лише якщо **всі** загубляться, ви дізнаєтесь про помилку. Підсумок: | Властивість | Значення | | --- | --- | | **Що робить** | Повертає перший **успішно виконаний** проміс | | **Якщо всі відхилені** | Повертає `AggregateError` з причинами | | **Тип поверненого значення** | `Promise` | | **Схожий на** | `Promise.race()`, але ігнорує `reject` | | **Типові сценарії** | fallback запити, резервні джерела даних, надлишкові запити до API | ### Типові помилки - **Очікувати звичайну помилку в `catch`.** Приходить `AggregateError`, а конкретні причини лежать у `err.errors`, тому `err.message` сам по собі майже нічого не пояснює. - **Плутати з `Promise.race()`.** `race()` впаде на першому ж відхиленні, `any()` же продовжить чекати на успіх. - **Забувати про підтримку.** `Promise.any()` це ES2021: у дуже старих середовищах потрібен поліфіл. - **Думати, що програні запити скасовуються.** Решта промісів виконується далі і навантажує мережу, для справжнього скасування потрібен `AbortController`. - **Передавати порожній масив.** `Promise.any([])` одразу відхиляється з `AggregateError`, бо успішному промісу нема звідки взятися. - **Вважати, що `fetch` відхиляється на помилці сервера.** Відповідь зі статусом 500 для `fetch` успішна, тому в fallback сценарії статус треба перевіряти вручну і кидати помилку самому.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.