update promises

This commit is contained in:
2025-03-05 16:17:27 +03:00
parent 8b08f95ce0
commit b4f0c9b803
12 changed files with 346 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
---
sidebar_position: 1
---
# Введение: колбэки
Источник *Deepseek*
Асинхронное программирование с использованием callback-функций в JavaScript — это один из традиционных подходов для работы с асинхронными операциями, такими как чтение файлов, запросы к серверу или таймеры. Callback-функция передается в качестве аргумента в асинхронную функцию и вызывается после завершения этой операции.
Вот пример асинхронного программирования с использованием callback:
```js
// Пример асинхронной функции, которая имитирует запрос к серверу
function fetchData(callback) {
// Имитируем задержку с помощью setTimeout
setTimeout(() => {
const data = { name: "John", age: 30 };
// Вызываем callback-функцию и передаем ей данные
callback(data);
}, 2000); // Задержка 2 секунды
}
// Callback-функция, которая будет вызвана после получения данных
function handleData(data) {
console.log("Данные получены:", data);
}
// Вызов асинхронной функции с передачей callback
fetchData(handleData);
console.log("Ожидание данных...");
```
Объяснение:
1. **fetchData** — это асинхронная функция, которая имитирует запрос к серверу с задержкой в 2 секунды.
2. **callback** — это функция, которая передается в `fetchData` и будет вызвана после завершения асинхронной операции.
3. **handleData** — это callback-функция, которая обрабатывает полученные данные.
4. **setTimeout** используется для имитации задержки, как будто данные загружаются с сервера.
Вывод в консоль:
```
Ожидание данных...
Данные получены: { name: "John", age: 30 }
```
Особенности:
- Callback-функции могут привести к так называемому "callback hell" (ад колбэков), когда много асинхронных операций вложены друг в друга, что делает код сложным для чтения и поддержки.
- В современных JavaScript-проектах вместо callback-функций часто используют Promises или async/await для более удобной работы с асинхронным кодом.

View File

@@ -0,0 +1,85 @@
---
sidebar_position: 2
---
# Promises
Источник *Deepseek*
Использование Promises (обещаний) в JavaScript — это более современный и удобный способ работы с асинхронными операциями по сравнению с callback-функциями. Promises позволяют избежать "callback hell" и делают код более читаемым и структурированным.
Вот пример асинхронного программирования с использованием Promises:
```js
// Пример асинхронной функции, которая возвращает Promise
function fetchData() {
return new Promise((resolve, reject) => {
// Имитируем задержку с помощью setTimeout
setTimeout(() => {
const success = true; // Имитируем успешное выполнение или ошибку
if (success) {
const data = { name: "Alice", age: 25 };
resolve(data); // Успешное выполнение
} else {
reject("Ошибка: Данные не получены"); // Ошибка
}
}, 2000); // Задержка 2 секунды
});
}
// Использование Promise
fetchData()
.then((data) => {
console.log("Данные получены:", data);
})
.catch((error) => {
console.error(error);
})
.finally(() => {
console.log("Запрос завершен.");
});
console.log("Ожидание данных...");
```
Объяснение:
1. **fetchData** — это функция, которая возвращает `Promise`. Внутри `Promise` выполняется асинхронная операция (в данном случае, с задержкой в 2 секунды).
2. **resolve** — вызывается, если операция завершилась успешно. В этом случае `Promise` переходит в состояние "fulfilled" (выполнено), и данные передаются в метод .then().
3. **reject** — вызывается, если произошла ошибка. В этом случае `Promise` переходит в состояние "rejected" (отклонено), и ошибка передается в метод .catch().
4. **.then()** — обрабатывает успешный результат.
5. **.catch()** — обрабатывает ошибки.
6. **.finally()** — выполняется в любом случае, независимо от того, завершился `Promise` успешно или с ошибкой.
#### Вывод в консоль:
```
Ожидание данных...
Данные получены: { name: "Alice", age: 25 }
Запрос завершен.
```
#### Если произойдет ошибка:
Если переменная `success` будет равна `false`, то вывод будет таким:
```
Ожидание данных...
Ошибка: Данные не получены
Запрос завершен.
```
#### Преимущества Promises:
- Более читаемый и структурированный код.
- Легко комбинировать несколько асинхронных операций с помощью `.then()`.
- Удобная обработка ошибок через `.catch()`.
#### Пример с цепочкой Promises:
```js
fetchData()
.then((data) => {
console.log("Данные получены:", data);
return data.age; // Возвращаем только возраст
})
.then((age) => {
console.log("Возраст пользователя:", age);
})
.catch((error) => {
console.error(error);
});
```
Этот пример показывает, как можно последовательно обрабатывать данные с помощью цепочки `.then()`.
💥 **Thenable**
> Если быть более точными, обработчик может возвращать не именно промис, а любой объект, содержащий метод .then, такие объекты называют «thenable», и этот объект будет обработан как промис.

View File

@@ -0,0 +1,52 @@
---
sidebar_position: 3
---
# Обработка ошибок
Источник: [https://learn.javascript.ru/promise-error-handling](https://learn.javascript.ru/promise-error-handling)
- `.catch` перехватывает все виды ошибок в промисах: будь то вызов `reject()` или ошибка, брошенная в обработчике при помощи `throw`.
- `.then` также перехватывает ошибки таким же образом, если задан второй аргумент (который является обработчиком ошибок).
- Необходимо размещать `.catch` там, где мы хотим обработать ошибки и знаем, как это сделать. Обработчик может проанализировать ошибку (могут быть полезны пользовательские классы ошибок) и пробросить её, если ничего не знает о ней (возможно, это программная ошибка).
- Можно и совсем не использовать `.catch`, если нет нормального способа восстановиться после ошибки.
- В любом случае нам следует использовать обработчик события `u`nhandledrejection` (для браузеров и аналог для других окружений), чтобы отслеживать необработанные ошибки и информировать о них пользователя (и, возможно, наш сервер), благодаря чему наше приложение никогда не будет «просто умирать».
## Неявный try…catch
Вокруг функции промиса и обработчиков находится `«невидимый try..catch»`. Если происходит исключение, то оно перехватывается, и промис считается отклонённым с этой ошибкой.
«Невидимый try..catch» вокруг промиса автоматически перехватывает ошибку и превращает её в отклонённый промис.
Это работает не только в функции промиса, но и в обработчиках. Если мы бросим ошибку (throw) из обработчика (.then), то промис будет считаться отклонённым, и управление перейдёт к ближайшему обработчику ошибок.
## Пробрасывание ошибок
Мы можем иметь столько обработчиков `.then`, сколько мы хотим, и затем использовать один `.catch` в конце, чтобы перехватить ошибки из всех обработчиков.
В обычном `try..catch` мы можем проанализировать ошибку и повторно пробросить дальше, если не можем её обработать. То же самое возможно для промисов.
Если мы пробросим `(throw)` ошибку внутри блока `.catch`, то управление перейдёт к следующему ближайшему обработчику ошибок. А если мы обработаем ошибку и завершим работу обработчика нормально, то продолжит работу ближайший успешный обработчик `.then`.
## Необработанные ошибки
Что произойдёт, если ошибка не будет обработана? \
В случае ошибки выполнение должно перейти к ближайшему обработчику ошибок.
Что происходит, когда обычная ошибка не перехвачена try..catch? Скрипт умирает с сообщением в консоли. Похожее происходит и в случае необработанной ошибки промиса.
JavaScript-движок отслеживает такие ситуации и генерирует в этом случае глобальную ошибку. Вы можете увидеть её в консоли, если запустите пример выше.
В браузере мы можем поймать такие ошибки, используя событие unhandledrejection:
```js
window.addEventListener('unhandledrejection', function(event) {
// объект события имеет два специальных свойства:
alert(event.promise); // [object Promise] - промис, который сгенерировал ошибку
alert(event.reason); // Error: Ошибка! - объект ошибки, которая не была обработана
});
new Promise(function() {
throw new Error("Ошибка!");
}); // нет обработчика ошибок
```
Если происходит ошибка, и отсутствует её обработчик, то генерируется событие `unhandledrejection`, и соответствующий объект `event` содержит информацию об ошибке.
Обычно такие ошибки неустранимы, поэтому лучше всего информировать пользователя о проблеме и, возможно, отправить информацию об ошибке на сервер.

View File

@@ -0,0 +1,18 @@
---
sidebar_position: 4
---
# Promise API
Источник:
В классе Promise есть 6 статических методов.
- **Promise.all(promises)** ожидает выполнения всех промисов и возвращает массив с результатами. Если любой из указанных промисов вернёт ошибку, то результатом работы Promise.all будет эта ошибка, результаты остальных промисов будут игнорироваться.
- **Promise.allSettled(promises)** (добавлен недавно) ждёт, пока все промисы завершатся и возвращает их результаты в виде массива с объектами, у каждого объекта два свойства:
- status: "fulfilled", если выполнен успешно или "rejected", если ошибка,
- value результат, если успешно или reason ошибка, если нет.
- **Promise.race(promises)** ожидает первый выполненный промис, который становится его результатом, остальные игнорируются.
- **Promise.any(promises)** (добавлен недавно) ожидает первый успешно выполненный промис, который становится его результатом, остальные игнорируются. Если все переданные промисы отклонены, AggregateError становится ошибкой `Promise.any`.
- **Promise.resolve(value)** возвращает успешно выполнившийся промис с результатом value.
- **Promise.reject(error)** возвращает промис с ошибкой error.
Из всех перечисленных методов, самый часто используемый **Promise.all**.

View File

@@ -0,0 +1,41 @@
---
sidebar_position: 5
---
# Промисификация
Источник: [https://learn.javascript.ru/promisify](https://learn.javascript.ru/promisify)
Промисификация это длинное слово для простого преобразования. Мы берём функцию, которая принимает колбэк и меняем её, чтобы она вместо этого возвращала промис.
Такие преобразования часто необходимы в реальной жизни, так как многие функции и библиотеки основаны на колбэках, а использование промисов более удобно, поэтому есть смысл «промисифицировать» их.
💥 **На заметку:**
- Промисификация это отличный подход, особенно, если вы будете использовать async/await (см. следующую главу об Async/await) но она не является тотальной заменой любых колбэков.
- Помните, промис может иметь только один результат, но колбэк технически может вызываться сколько угодно раз.
- Поэтому промисификация используется для функций, которые вызывают колбэк только один раз. Последующие вызовы колбэка будут проигнорированы.
```js
// promisify(f, true), чтобы получить массив результатов
function promisify(f, manyArgs = false) {
return function (...args) {
return new Promise((resolve, reject) => {
function callback(err, ...results) { // наш специальный колбэк для f
if (err) {
reject(err);
} else {
// делаем resolve для всех results колбэка, если задано manyArgs
resolve(manyArgs ? results : results[0]);
}
}
args.push(callback);
f.call(this, ...args);
});
};
};
// использование:
f = promisify(f, true);
f(...).then(arrayOfResults => ..., err => ...)
```

View File

@@ -0,0 +1,19 @@
---
sidebar_position: 6
---
# Async/await
По сути, это просто «синтаксический сахар» для получения результата промиса, более наглядный, чем `promise.then`.
Ключевое слово `async` перед объявлением функции:
1. Обязывает её всегда возвращать промис.
2. Позволяет использовать await в теле этой функции.
Ключевое слово `await` перед промисом заставит JavaScript дождаться его выполнения, после чего:
1. Если промис завершается с ошибкой, будет сгенерировано исключение, как если бы на этом месте находилось `throw`.
2. Иначе вернётся результат промиса.
Вместе они предоставляют отличный каркас для написания асинхронного кода. Такой код легко и писать, и читать.
Хотя при работе с `async/await` можно обходиться без `promise.then/catch`, иногда всё-таки приходится использовать эти методы (на верхнем уровне вложенности, например). Также `await` отлично работает в сочетании с `Promise.all`, если необходимо выполнить несколько задач параллельно.