From b4f0c9b803f8754cf74a415b6300f89e19a43faf Mon Sep 17 00:00:00 2001 From: RedrockJS Date: Wed, 5 Mar 2025 16:17:27 +0300 Subject: [PATCH] update promises --- .../{07-try-catch => 07-errors}/.gitkeep | 0 docs/javascript/07-errors/01-try-catch.md | 74 ++++++++++++++++ docs/javascript/07-errors/02-extend-errors.md | 11 +++ .../_category_.json | 0 docs/javascript/07-try-catch/index.md | 0 docs/javascript/08-promises/01-callbacks.md | 46 ++++++++++ docs/javascript/08-promises/02-promises.md | 85 +++++++++++++++++++ .../08-promises/03-promises-error.md | 52 ++++++++++++ docs/javascript/08-promises/04-promise-api.md | 18 ++++ .../08-promises/05-promisification.md | 41 +++++++++ docs/javascript/08-promises/06-acync-await.md | 19 +++++ docs/javascript/08-promises/index.md | 0 12 files changed, 346 insertions(+) rename docs/javascript/{07-try-catch => 07-errors}/.gitkeep (100%) create mode 100644 docs/javascript/07-errors/01-try-catch.md create mode 100644 docs/javascript/07-errors/02-extend-errors.md rename docs/javascript/{07-try-catch => 07-errors}/_category_.json (100%) delete mode 100644 docs/javascript/07-try-catch/index.md create mode 100644 docs/javascript/08-promises/01-callbacks.md create mode 100644 docs/javascript/08-promises/02-promises.md create mode 100644 docs/javascript/08-promises/03-promises-error.md create mode 100644 docs/javascript/08-promises/04-promise-api.md create mode 100644 docs/javascript/08-promises/05-promisification.md create mode 100644 docs/javascript/08-promises/06-acync-await.md delete mode 100644 docs/javascript/08-promises/index.md diff --git a/docs/javascript/07-try-catch/.gitkeep b/docs/javascript/07-errors/.gitkeep similarity index 100% rename from docs/javascript/07-try-catch/.gitkeep rename to docs/javascript/07-errors/.gitkeep diff --git a/docs/javascript/07-errors/01-try-catch.md b/docs/javascript/07-errors/01-try-catch.md new file mode 100644 index 0000000..eb3dc66 --- /dev/null +++ b/docs/javascript/07-errors/01-try-catch.md @@ -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** - Объект ошибки. diff --git a/docs/javascript/07-errors/02-extend-errors.md b/docs/javascript/07-errors/02-extend-errors.md new file mode 100644 index 0000000..7885aca --- /dev/null +++ b/docs/javascript/07-errors/02-extend-errors.md @@ -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` в примерах выше, но это не обязательно. + diff --git a/docs/javascript/07-try-catch/_category_.json b/docs/javascript/07-errors/_category_.json similarity index 100% rename from docs/javascript/07-try-catch/_category_.json rename to docs/javascript/07-errors/_category_.json diff --git a/docs/javascript/07-try-catch/index.md b/docs/javascript/07-try-catch/index.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/javascript/08-promises/01-callbacks.md b/docs/javascript/08-promises/01-callbacks.md new file mode 100644 index 0000000..927cfc4 --- /dev/null +++ b/docs/javascript/08-promises/01-callbacks.md @@ -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 для более удобной работы с асинхронным кодом. \ No newline at end of file diff --git a/docs/javascript/08-promises/02-promises.md b/docs/javascript/08-promises/02-promises.md new file mode 100644 index 0000000..908b48f --- /dev/null +++ b/docs/javascript/08-promises/02-promises.md @@ -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», и этот объект будет обработан как промис. diff --git a/docs/javascript/08-promises/03-promises-error.md b/docs/javascript/08-promises/03-promises-error.md new file mode 100644 index 0000000..d57806b --- /dev/null +++ b/docs/javascript/08-promises/03-promises-error.md @@ -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` содержит информацию об ошибке. + +Обычно такие ошибки неустранимы, поэтому лучше всего – информировать пользователя о проблеме и, возможно, отправить информацию об ошибке на сервер. + diff --git a/docs/javascript/08-promises/04-promise-api.md b/docs/javascript/08-promises/04-promise-api.md new file mode 100644 index 0000000..095af17 --- /dev/null +++ b/docs/javascript/08-promises/04-promise-api.md @@ -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**. \ No newline at end of file diff --git a/docs/javascript/08-promises/05-promisification.md b/docs/javascript/08-promises/05-promisification.md new file mode 100644 index 0000000..1809eee --- /dev/null +++ b/docs/javascript/08-promises/05-promisification.md @@ -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 => ...) +``` \ No newline at end of file diff --git a/docs/javascript/08-promises/06-acync-await.md b/docs/javascript/08-promises/06-acync-await.md new file mode 100644 index 0000000..ada4728 --- /dev/null +++ b/docs/javascript/08-promises/06-acync-await.md @@ -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`, если необходимо выполнить несколько задач параллельно. \ No newline at end of file diff --git a/docs/javascript/08-promises/index.md b/docs/javascript/08-promises/index.md deleted file mode 100644 index e69de29..0000000