diff --git a/files/uk/web/javascript/reference/global_objects/promise/promise/index.md b/files/uk/web/javascript/reference/global_objects/promise/promise/index.md new file mode 100644 index 0000000000..2672262b59 --- /dev/null +++ b/files/uk/web/javascript/reference/global_objects/promise/promise/index.md @@ -0,0 +1,225 @@ +--- +title: Конструктор Promise() +slug: Web/JavaScript/Reference/Global_Objects/Promise/Promise +page-type: javascript-constructor +browser-compat: javascript.builtins.Promise.Promise +--- + +{{JSRef}} + +Конструктор **`Promise()`** (проміс) створює об'єкти {{jsxref("Promise")}}. Він використовується в основному для обгортання API, які використовують зворотні виклики, але ще не підтримують проміси. + +{{EmbedInteractiveExample("pages/js/promise-constructor.html", "taller")}} + +## Синтаксис + +```js-nolint +new Promise(executor) +``` + +> **Примітка:** `Promise()` можна конструювати лише за допомогою [`new`](/uk/docs/Web/JavaScript/Reference/Operators/new). Спроба викликати його без `new` викидає {{jsxref("TypeError")}}. + +### Параметри + +- `executor` + - : {{jsxref("function", "Функція")}}, що викликається конструктором. Вона отримує як параметри дві функції: `resolveFunc` і `rejectFunc`. Будь-які помилки, що виникають в `executor`, призводять до відхилення проміса, а повернене значення ігнорується. Семантика `executor` детально описана нижче. + +### Повернене значення + +Бувши викликаним за допомогою `new`, конструктор `Promise` повертає об'єкт проміса. Об'єкт проміса стає _вирішеним_, коли закликається або функція `resolveFunc`, або функція `rejectFunc`. Зверніть увагу, що якщо викликати `resolveFunc` або `rejectFunc` і передати інший об'єкт `Promise` як аргумент, то можна сказати, що проміс стає "вирішеним", але ще не "залагодженим". Дивіться [опис Promise](/uk/docs/Web/JavaScript/Reference/Global_Objects/Promise#opys) для отримання додаткових пояснень. + +## Опис + +Традиційно (до промісів) асинхронні задачі оброблялися за допомогою зворотних викликів. + +```js +readFile("./data.txt", (error, result) => { + // Ця функція зворотного виклику викликається, коли завдання виконано, з + // результатом – `error` або `result`. Будь-яка операція, що залежить від + // результату, повинна бути визначена в цій функції зворотного виклику. +}); +// Код тут викликається зразу після того, як запит `readFile` +// запускається. Він не чекає виклику функції зворотного виклику, таким чином +// роблячи `readFile` "асинхронним". +``` + +Щоб скористатися перевагами поліпшення читабельності та можливостей мови, які пропонують проміси, конструктор `Promise()` дозволяє перетворити API на основі зворотних викликів на API на основі промісів. + +> **Примітка:** Якщо ваша задача вже заснована на промісах, вам, ймовірно, не потрібен конструктор `Promise()`. + +Функція `executor` – це клієнтський код, що зв'язує результат виклику функції зворотного виклику з промісом. Ви, програміст, пишете функцію `executor`. Очікується, що її сигнатура буде такою: + +```js +function executor(resolveFunc, rejectFunc) { + // Зазвичай – якась асинхронна операція, що приймає функцію зворотного виклику, + // подібна до функції `readFile` вище +} +``` + +Параметри `resolveFunc` і `rejectFunc` також є функціями, і їм можна дати будь-які фактичні імена. Їх сигнатури прості: вони приймають один параметр будь-якого типу. + +```js +resolveFunc(value); // виклик при вирішенні +rejectFunc(reason); // виклик при відхиленні +``` + +Параметр `value`, переданий до `resolveFunc`, може бути ще одним об'єктом-промісом, і в такому випадку стан новоствореного проміса буде "зав'язаний" на стан переданого проміса (як частина проміса [вирішення](#funktsiia-resolve)). Функція `rejectFunc` має семантику, близьку до інструкції [`throw`](/uk/docs/Web/JavaScript/Reference/Statements/throw), тому `reason` зазвичай є примірником [`Error`](/uk/docs/Web/JavaScript/Reference/Global_Objects/Error). Якщо `value` або `reason` відсутні, то проміс сповнюється або відхиляється з `undefined`. + +Стан завершення `executor` має обмежений вплив на стан проміса: + +- Повернене з `executor` значення ігнорується. Інструкції `return` всередині `executor` впливають лише на потік керування і змінюють те, чи виконуються певні частини функції, але не мають жодного впливу на значення сповнення проміса. Якщо `executor` завершується і неможливо, щоб `resolveFunc` або `rejectFunc` були викликані в майбутньому (наприклад, немає запланованих асинхронних задач), то такий проміс залишається назавжди в стані очікування. +- Якщо помилка викидається всередині `executor`, то проміс відхиляється, якщо `resolveFunc` і `rejectFunc` ще не були викликані. + +> **Примітка:** Існування промісів у стані очікування не заважає програмі завершитися. Якщо цикл подій порожній, програма завершується, не зважаючи на будь-які проміси у стані очікування (тому що вони обов'язково залишаються у стані очікування назавжди). + +Ось нарис типового плину виконання: + +1. Коли конструктор породжує новий об'єкт `Promise`, він також породжує відповідну пару функцій `resolveFunc` і `rejectFunc`; вони "припнуті" до об'єкта `Promise`. +2. Функція `executor` зазвичай обгортає якусь асинхронну операцію, що надає API на основі зворотного виклику. Функція зворотного виклику (та, що передається в оригінальний API на основі зворотних викликів) визначається всередині коду `executor`, тому вона має доступ до `resolveFunc` і `rejectFunc`. +3. Функція `executor` викликається синхронно (як тільки `Promise` створено) з функціями `resolveFunc` і `rejectFunc` як аргументами. +4. Код всередині `executor` має можливість виконати якусь операцію. Примірник проміса повідомляється про завершення асинхронної задачі за допомогою побічного ефекту, спричиненого `resolveFunc` або `rejectFunc`. Побічний ефект полягає в тому, що об'єкт `Promise` стає "вирішеним". + - Якщо спершу викликається `resolveFunc`, то передане значення буде [вирішенням](#funktsiia-resolve). Проміс може залишитися у стані очікування (якщо передається інший [очікуваний об'єкт](/uk/docs/Web/JavaScript/Reference/Global_Objects/Promise#ochikuvani-obiekty)), стати сповненим (у більшості випадків, коли передається не очікуване значення) або відхилитися (у випадку недійсного значення вирішення). + - Якщо першою викликається `rejectFunc`, то проміс миттєво відхиляється. + - Коли вже була викликана одна з функцій вирішення (`resolveFunc` або `rejectFunc`), проміс залишається вирішеним. Тільки перший виклик `resolveFunc` або `rejectFunc` впливає на кінцевий стан проміса, і наступні виклики жодним чином не можуть ані змінити значення сповнення чи причину відхилення, ані перемкнути його кінцевий стан зі "сповненого" на "відхилений" або навпаки. + - Якщо `executor` завершується викиданням помилки, то проміс відхиляється. Однак помилка ігнорується, якщо одна з функцій вирішення вже була викликана (таким чином, проміс вже вирішено). + - Вирішення проміса не обов'язково змушує проміс стати сповненим або відхиленим (тобто залагодженим). Цей проміс може залишитися в стані очікування, якщо його вирішено іншим очікуваним об'єктом, але його кінцевий стан буде відповідати кінцевому стану цього вирішеного очікуваного об'єкта. +5. Коли проміс залагоджено, він (асинхронно) закликає всі подальші обробники, прив'язані за допомогою {{jsxref("Promise/then", "then()")}}, {{jsxref("Promise/catch", "catch()")}} або {{jsxref("Promise/finally", "finally()")}}. Значення сповнення або причина відхилення передаються виклику обробників сповнення та відхилення як вхідний параметр (дивіться [Ланцюжки промісів](/uk/docs/Web/JavaScript/Reference/Global_Objects/Promise#lantsiuzhky-promisiv)). + +Наприклад, API `readFile` на основі зворотних викликів вище можна перетворити на API на основі промісів. + +```js +const readFilePromise = (path) => + new Promise((resolve, reject) => { + readFile(path, (error, result) => { + if (error) { + reject(error); + } else { + resolve(result); + } + }); + }); + +readFilePromise("./data.txt") + .then((result) => console.log(result)) + .catch((error) => console.error("Не вийшло прочитати дані")); +``` + +Функції зворотного виклику `resolve` і `reject` доступні лише всередині області видимості функції `executor`, тобто не можна звернутися до них після конструювання проміса. Якщо потрібно сконструювати проміс до прийняття рішення щодо того, як його вирішувати, можна натомість скористатися методом {{jsxref("Promise.withResolvers")}}, який видає функції `resolve` і `reject`. + +### Функція resolve + +Функція `resolve` має наступну логіку: + +- Якщо вона викликана з таким же значенням, як і новостворений проміс (проміс, до якого вона "припнута"), то цей проміс відхиляється з помилкою {{jsxref("TypeError")}}. +- Якщо вона викликається з не [очікуваним](/uk/docs/Web/JavaScript/Reference/Global_Objects/Promise#ochikuvani-obiekty) значенням (примітивом, або об'єктом, чию властивість `then` не можна викликати, в тому числі коли вона відсутня), то її проміс негайно сповнюється цим значенням. +- Якщо вона викликається з очікуваним значенням (наприклад, іншим примірником `Promise`), то зберігається метод `then` цього значення – він буде викликаний у майбутньому (і завжди асинхронно). Цей метод `then` буде викликаний з двома функціями зворотного виклику, які будуть двома новими функціями з такою ж логікою, як у функцій `resolveFunc` і `rejectFunc`, переданих у функцію `executor`. Якщо виклик цього методу `then` викидає помилку, то поточний проміс відхиляється з викинутою помилкою. + +В останньому випадку, це означає, що подібний код: + +```js +new Promise((resolve, reject) => { + resolve(thenable); +}); +``` + +Приблизно рівносильний такому: + +```js +new Promise((resolve, reject) => { + try { + thenable.then( + (value) => resolve(value), + (reason) => reject(reason), + ); + } catch (e) { + reject(e); + } +}); +``` + +Окрім того, що в випадку `resolve(thenable)`: + +1. Функція `resolve` викликається синхронно, тож повторний виклик `resolve` або `reject` ніяк не діє, навіть коли обробники, приєднані за допомогою `anotherPromise.then()`, ще не викликані. +2. Метод `then` викликається асинхронно, тож проміс ніколи не буде миттєво сповненим, якщо передається очікуваний об'єкт. + +У зв'язку з тим, що `resolve` викликається знову з тим, що `thenable.then()` передає йому як `value`, функція вирішення може сплющувати вкладені очікувані об'єкти, де очікуваний об'єкт викликає свій обробник `onFulfilled` з іншим очікуваним об'єктом. Це означає, що обробник сповнення справжнього проміса ніколи не отримає очікуваний об'єкт як значення сповнення. + +## Приклади + +### Перетворення API на основі зворотних викликів на API на основі промісів + +Щоб надати функції функціональність промісів, вона повинна повертати проміс, викликаючи функції `resolve` і `reject` у відповідний час. + +```js +function myAsyncFunction(url) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open("GET", url); + xhr.onload = () => resolve(xhr.responseText); + xhr.onerror = () => reject(xhr.statusText); + xhr.send(); + }); +} +``` + +### Вплив виклику resolveFunc + +Виклик `resolveFunc` змушує проміс стати вирішеним, тож подальші виклики `resolveFunc` або `rejectFunc` не мають жодного впливу. Однак проміс може перебувати в будь-якому стані: очікування, сповненості або відхиленості + +Цей проміс `pendingResolved` вирішений в момент створення, тому що він вже "замкнений" на відповідність остаточному станові внутрішнього проміса, і подальший виклик `resolveOuter` або `rejectOuter` або викидання помилки пізніше в функції-виконавці не має жодного впливу на його остаточний стан. Однак внутрішній проміс все ж перебуває в стані очікування, поки не мине 100 мс, тож зовнішній проміс також перебуває у стані очікування: + +```js +const pendingResolved = new Promise((resolveOuter, rejectOuter) => { + resolveOuter( + new Promise((resolveInner) => { + setTimeout(() => { + resolveInner("внутрішній"); + }, 100); + }), + ); +}); +``` + +Цей проміс `fulfilledResolved` стає сповненим в момент вирішення, тому що він вирішується значенням, яке не є очікуваним об'єктом. Однак коли він створюється, він є невирішеним, тому що ані `resolve`, ані `reject` ще не були викликані. Невирішений проміс обов'язково перебуває в стані очікування: + +```js +const fulfilledResolved = new Promise((resolve, reject) => { + setTimeout(() => { + resolve("зовнішній"); + }, 100); +}); +``` + +Виклик `rejectFunc` очевидно призводить до відхилення проміса. Однак є ще два способи, якими можна змусити проміс миттєво відхилитися, навіть коли викликається функція `resolveFunc`. + +```js +// 1. Вирішення проміса самим собою +const rejectedResolved1 = new Promise((resolve) => { + // Примітка: resolve має бути викликана асинхронно, + // щоб ініціалізувалася змінна rejectedResolved1 + setTimeout(() => resolve(rejectedResolved1)); // TypeError: Chaining cycle detected for promise # +}); + +// 2. Вирішення об'єктом, що викидає помилку, коли відбувається звертання до властивості `then` +const rejectedResolved2 = new Promise((resolve) => { + resolve({ + get then() { + throw new Error("Не можна отримати властивість then"); + }, + }); +}); +``` + +## Специфікації + +{{Specifications}} + +## Сумісність із браузерами + +{{Compat}} + +## Дивіться також + +- [Поліфіл `Promise` у складі `core-js`](https://github.com/zloirock/core-js#ecmascript-promise) +- Посібник [Використання промісів](/uk/docs/Web/JavaScript/Guide/Using_promises) +- {{jsxref("Promise.withResolvers()")}} diff --git a/uk_spelling_additions.txt b/uk_spelling_additions.txt index 96929771af..c160137221 100644 --- a/uk_spelling_additions.txt +++ b/uk_spelling_additions.txt @@ -1,5 +1,3 @@ -64-бітним -7-бітними Айка аксесор аксесора @@ -54,6 +52,7 @@ вимкненості вирізненність відхиленістю +відхиленості візуалізаційно-блокувальне Вірфса-Брока властивості-значення @@ -419,6 +418,7 @@ фронтенді фронтенду функцією-виконавцем +функції-виконавці функції-вирішувача функції-обробника функції-обробники