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,74 @@
---
sidebar_position: 1
---
# Обработка ошибок, "try..catch"
Конструкция `try..catch` позволяет обрабатывать ошибки во время исполнения кода. Она позволяет запустить код и перехватить ошибки, которые могут в нём возникнуть.
Секций `catch` или `finally` может не быть, то есть более короткие конструкции `try..catch` и `try..finally` также корректны.
Объекты ошибок содержат следующие свойства:
- `message` понятное человеку сообщение.
- `name` строка с именем ошибки (имя конструктора ошибки).
- `stack` (нестандартное, но хорошо поддерживается) стек на момент ошибки.
Если объект ошибки не нужен, мы можем пропустить его, используя `catch {` вместо `catch(err) {`.
Мы можем также генерировать собственные ошибки, используя оператор `throw`. Аргументом `throw` может быть что угодно, но обычно это объект ошибки, наследуемый от встроенного класса `Error`. Подробнее о расширении ошибок см. в следующей главе.
Проброс исключения это очень важный приём обработки ошибок: блок `catch` обычно ожидает и знает, как обработать определённый тип ошибок, поэтому он должен пробрасывать дальше ошибки, о которых он не знает.
Даже если у нас нет `try..catch`, большинство сред позволяют настроить «глобальный» обработчик ошибок, чтобы ловить ошибки, которые «выпадают наружу». В браузере это window.onerror.
**`try..catch` работает синхронно**
## try…catch…finally
```js
try {
//... пробуем выполнить код...
} catch(e) {
//... обрабатываем ошибки ...
} finally {
//... выполняем всегда ...
}
```
## Оператор «throw»
Оператор throw генерирует ошибку.
```js
let json = '{ "age": 30 }'; // данные неполны
try {
let user = JSON.parse(json); // <-- выполнится без ошибок
if (!user.name) {
throw new SyntaxError("Данные неполны: нет имени"); // (*)
}
alert( user.name );
} catch(e) {
alert( "JSON Error: " + e.message ); // JSON Error: Данные неполны: нет имени
}
```
## Проброс исключения
Блок `catch` должен обрабатывать только те ошибки, которые ему известны, и «пробрасывать» все остальные.
Техника «проброс исключения» выглядит так:
1. Блок `catch` получает все ошибки.
2. В блоке `catch(err) {...}` мы анализируем объект ошибки `err`.
3. Если мы не знаем как её обработать, тогда делаем `throw err`.
## Глобальный catch
Давайте представим, что произошла фатальная ошибка (программная или что-то ещё ужасное) снаружи try..catch, и скрипт упал.
Существует ли способ отреагировать на такие ситуации? Мы можем захотеть залогировать ошибку, показать что-то пользователю (обычно они не видят сообщение об ошибке) и т.д.
В Node.js для этого есть process.on("uncaughtException"). А в браузере мы можем присвоить функцию специальному свойству window.onerror, которая будет вызвана в случае необработанной ошибки.
```js
window.onerror = function(message, url, line, col, error) {
// ...
};
```
- **message** - Сообщение об ошибке.
- **url** - URL скрипта, в котором произошла ошибка.
- **line**, col - Номера строки и столбца, в которых произошла ошибка.
- **error** - Объект ошибки.

View File

@@ -0,0 +1,11 @@
---
sidebar_position: 2
---
# Пользовательские ошибки, расширение Error
Источник: [https://learn.javascript.ru/custom-errors](https://learn.javascript.ru/custom-errors)
- Мы можем наследовать свои классы ошибок от `Error` и других встроенных классов ошибок, но нужно позаботиться о свойстве name и не забыть вызвать `super`.
- Мы можем использовать `instanceof` для проверки типа ошибок. Это также работает с наследованием. Но иногда у нас объект ошибки, возникшей в сторонней библиотеке, и нет простого способа получить класс. Тогда для проверки типа ошибки можно использовать свойство `name`.
- Обёртывание исключений является распространённой техникой: функция ловит низкоуровневые исключения и создаёт одно «высокоуровневое» исключение вместо разных низкоуровневых. Иногда низкоуровневые исключения становятся свойствами этого объекта, как `err.cause` в примерах выше, но это не обязательно.

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`, если необходимо выполнить несколько задач параллельно.