-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
translation(JS): web/javascript/reference/global_objects/promise/prom…
…ise (#2616) * translation(JS): web/javascript/reference/global_objects/promise/promise * update(JS): web/javascript/reference/global_objects/promise/promise * Apply suggestions from code review Co-authored-by: Mykola Myslovskyi <[email protected]> --------- Co-authored-by: Mykola Myslovskyi <[email protected]>
- Loading branch information
1 parent
0b91025
commit 0359254
Showing
2 changed files
with
227 additions
and
2 deletions.
There are no files selected for viewing
225 changes: 225 additions & 0 deletions
225
files/uk/web/javascript/reference/global_objects/promise/promise/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 #<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()")}} |
Oops, something went wrong.