Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Promise.withResolvers()». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`Promise.withResolvers()` це статичний метод, який створює новий проміс і одразу повертає об'єкт `{ promise, resolve, reject }`. Тобто це офіційний спосіб отримати зовнішні `resolve` і `reject`, не пишучи `new Promise((resolve, reject) => { ... })` і не витягуючи функції з executor у зовнішні змінні. Executor тут взагалі не викликається: ви одразу отримуєте проміс разом із «пультом керування» ним.** ```javascript const { promise, resolve, reject } = Promise.withResolvers(); setTimeout(() => resolve('Успіх'), 1000); promise.then(console.log).catch(console.error); ``` **Ключове:** це стандартна заміна патерну deferred, чистіша і типобезпечніша за ручне присвоєння `resolve` у зовнішню змінну.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`Promise.withResolvers()` створює новий проміс і одразу віддає його разом із функціями `resolve` та `reject`, якими ним керують ззовні.** Це стандартна, безпечна заміна старому трюку з витягуванням `resolve` і `reject` з конструктора `new Promise`. ## Теорія ### TL;DR - Виклик `Promise.withResolvers()` повертає об'єкт `{ promise, resolve, reject }`. - Executor не викликається взагалі: немає колбека `(resolve, reject) => {}`, є лише готовий об'єкт. - Це офіційна форма патерну deferred: промісом керують з будь-якого місця коду після його створення. - Замінює старий прийом з двома зовнішніми змінними, який був неочевидним і погано типізувався. - Підтримка: Chrome 120+, Node.js 20.11+, Deno 1.41+, Safari 17.4+. ### Швидкий приклад ```javascript const { promise, resolve, reject } = Promise.withResolvers(); setTimeout(() => resolve('Успіх'), 1000); promise.then(console.log).catch(console.error); ``` Через секунду в консолі буде: ```javascript Успіх ``` Усе те саме, що раніше доводилося робити «вручну» через `new Promise`. ### Що саме повертає метод Метод створює **новий проміс** і одразу повертає об'єкт із **трьома властивостями**: | Властивість | Тип | Опис | | --- | --- | --- | | `promise` | `Promise` | Сам проміс, який можна повернути або дочекатися | | `resolve` | `Function` | Переводить проміс у стан `fulfilled` | | `reject` | `Function` | Переводить проміс у стан `rejected` | Тобто це офіційний спосіб отримати зовнішні `resolve` та `reject`, без `new Promise((resolve, reject) => { ... })`. Сигнатура, як її бачить TypeScript: ```javascript Promise.withResolvers<T>() => { promise: Promise<T>, resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void } ``` Це робить код безпечнішим: IDE знає тип `promise` і тип аргументу його `resolve`. ### Як це робили раніше До появи `Promise.withResolvers()` писали так: ```javascript let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); ``` Виглядало це неочевидно (особливо для TypeScript, де змінні лишалися з типом `undefined` до присвоєння) і було потенційно небезпечно: `resolve` і `reject` можна було випадково перевизначити. Тепер те саме робиться безпечно й декларативно: ```javascript const { promise, resolve, reject } = Promise.withResolvers(); ``` Проста аналогія: `Promise.withResolvers()` це проміс, який одразу йде в комплекті з пультом керування. Раніше кнопки `resolve` і `reject` доводилося виколупувати вручну, тепер вони є «з коробки». ### Практичні сценарії **Очікування події.** Класичний випадок, коли завершенням проміса треба керувати ззовні: ```javascript function waitForEvent(emitter, event) { const { promise, resolve } = Promise.withResolvers(); emitter.once(event, resolve); return promise; } ``` Тепер можна писати: ```javascript await waitForEvent(button, 'click'); console.log('Кнопку натиснуто'); ``` **Відкладені задачі (deferred).** Проміс створюють в одному місці, а завершують у зовсім іншому: ```javascript const deferred = Promise.withResolvers(); // десь у коді setTimeout(() => deferred.resolve('Готово'), 2000); // а в іншому місці deferred.promise.then(console.log); ``` Через 2 секунди в консолі буде `Готово`. **Комбінація з AbortController.** Зовнішній `reject` зручно чіпляти до сигналу скасування: ```javascript const controller = new AbortController(); const { promise, resolve, reject } = Promise.withResolvers(); controller.signal.addEventListener('abort', () => reject(new Error('Скасовано'))); setTimeout(() => resolve('Успішно'), 2000); // десь пізніше controller.abort(); promise.catch(console.error); ``` Виведе: ```javascript Error: Скасовано ``` ### Особливості поведінки і підтримка | Особливість | Опис | | --- | --- | | Створює новий `Promise` | без потреби вручну писати конструктор | | Зручно для патерну deferred | коли результат стане відомий пізніше | | Не викликає executor | жодних аргументів `(resolve, reject)`, лише об'єкт | | Дозволяє керувати ззовні | промісом можна керувати після його створення | Підсумкова таблиця: | Пункт | Значення | | --- | --- | | `Promise.withResolvers()` | Створює проміс і повертає `resolve` та `reject` | | **Повертає** | `{ promise, resolve, reject }` | | **Переваги** | Чистіше, безпечніше і типобезпечніше, ніж `new Promise(...)` | | **Використовують для** | Відкладених задач, подій, зовнішнього керування промісом | | **Підтримка** | Chrome 120+, Node.js 20.11+, Deno 1.41+, Safari 17.4+ | ### Типові помилки - **Забути про `catch` на `promise`.** Якщо десь викличуть `reject`, а обробника немає, отримаєте `unhandled rejection`. Це особливо легко проґавити, коли `reject` живе в іншому модулі. - **Створити проміс і ніколи його не завершити.** Якщо жоден `resolve` чи `reject` не викликається, `await` на ньому зависає назавжди, а разом із ним течуть замикання і підписки. - **Вважати, що повторний `resolve` щось змінює.** Проміс завершується один раз: другий виклик `resolve` або `reject` мовчки ігнорується. - **Використовувати там, де вистачає звичайного `new Promise`.** Якщо вся асинхронна логіка вміщається в executor, зовнішні `resolve` і `reject` лише розмазують керування по коду. - **Розраховувати на метод у старих середовищах.** Це відносно свіжа можливість: для старих браузерів або Node.js потрібен поліфіл чи звичний `new Promise`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.