Не используйте вложенные обратные вызовы, вместо этого используйте цепочку промисов

Не используйте вложенные обратные вызовы, вместо этого используйте цепочку промисов

26 мая 2022 г.

Если вы занимаетесь веб-разработкой на JavaScript, я думаю, вы уже знакомы с Promise и неоднократно сталкивались с адом обратных вызовов. Цепочка промисов в JavaScript — это один из способов решить проблему ада обратных вызовов, и мы обсудим его в этой статье. Тем не менее, давайте немного подытожим для тех, кто не знаком с понятиями.


Что такое обещание JavaScript?


В JavaScript (ES6 и выше) Promise — это объект, представляющий состояние и результат асинхронных операций, таких как вызов API или чтение/запись ввода-вывода. Состояния включают ожидание, выполнено и отклонено.


  • Ожидание: операция выполняется, и результат не возвращен.

  • Выполнено: операция прошла успешно, результат возвращен.

  • Отклонено: операция завершилась неудачно, и была возвращена ошибка.

У Promise есть два метода: then и catch. Метод then принимает обратный вызов действия, которое будет инициировано после выполнения обещания, в то время как catch запускается всякий раз, когда обещание отклонено.


```javascript


// Операция асинхронного вызова API


getWeatherTodayPromise


.then((weatherForecast) => { // Выполнено


// Синхронная работа


дисплей (прогноз погоды)


.catch((error) = > { // Отклонено


console.error(ошибка)


Что такое ад обратного вызова?


Короче говоря, это когда ваши обратные вызовы были вложены на несколько уровней до такой степени, что они становятся неуправляемыми. Это может произойти в любом языке программирования и чаще встречается в асинхронных операциях. Глубоко вложенные промис и обратный вызов в JavaScript — это всего лишь один из вариантов ада обратных вызовов, ​​и примеры в статье основаны на дисперсии этого ада обратных вызовов.


```javascript


// Типичный ад обратного вызова, который включает несколько промисов JavaScript


первое обещание


.затем (второе обещание


.затем(третье обещание


.тогда(...).поймать(...)


).ловить(...)


).ловить(...)


Читабельность — это одно, но ад обратных вызовов может вызвать другие проблемы с областью видимости. Типичным является сокрытие (проглатывание) ошибок, когда ошибка, вызванная внутренним обещанием, не была обнаружена.


```javascript


// Операция асинхронного вызова API


getWeatherTodayPromise


.тог((прогноз погоды) => {


// Асинхронная операция ввода-вывода


writeWeatherForecastToLogFilePromise(weatherForecast) // FAILED


// В отличие от try-catch, здесь нет внешнего универсального решения


// У вас должен быть "улов" на каждом вложенном промисе


// В противном случае промис не завершается, и информация об ошибке теряется


.catch((ошибка) => {


// Здесь перехватывается ошибка ввода-вывода


console.error("Внутреннее обещание", ошибка)


.catch((ошибка) = > {


// ошибка ввода-вывода здесь НЕ перехватывается


console.error("Внешнее обещание", ошибка)


Что такое цепочка промисов в JavaScript?


Вы только что узнали, что ад обратных вызовов указывает на неуправляемые вложенные уровни обратных вызовов. С учетом сказанного, один из способов решить проблему — сделать обратные вызовы НЕ вложенными. Сделайте его мелким!


Сцепление промисов в JavaScript — это когда несколько методов then и catch вызываются последовательно для полного удаления вложенных уровней, сохраняя при этом предполагаемые результаты. Можно сказать, что это один из способов рефакторинга вашей кодовой базы.


```javascript


// Цепочка обещаний


первое обещание


.then(() => второе обещание)


.then(() => третье обещание)


.потом(...)


.ловить(...)


.ловить(...)


.ловить(...)


.потом(...)


// Типичный ад обратного вызова, который включает несколько промисов JavaScript


первое обещание


.затем (второе обещание


.затем(третье обещание


.тогда(...).поймать(...)


).ловить(...)


).ловить(...)


Если вы имеете дело с обычными функциями на основе обратного вызова (НЕ промисами), вам нужно сначала промисифицировать функции, чтобы применить цепочку промисов. Есть способы сделать это, например, с помощью библиотеки es6-promisify.


Как работает цепочка промисов в JavaScript?


  • then и catch являются методами объекта Promise, поэтому для создания цепочки обратный вызов в методе then должен возвращать новый Promise.

```javascript


// Правильная реализация


// "() => что-то" является сокращением для "() => {возвратить что-то})


const secondPromise = firstPromise.then(() => newPromise)


secondPromise.then(() => другоеPromise).then(...)


// Неправильная реализация и возникает исключение


firstPromise.then(() => null).then(...)


  • Результат обещания переносится на следующее then.

```javascript


getWeatherTodayPromise


.then(weatherForecastResult => writeWeatherForecastToLogFilePromise(weatherForecastResult))


// writeWeatherForecastToLogFilePromise(weatherForecastResult), если он выполнен, выдаст "ioWriteResult"


.then(ioWriteResult => Promise.all([


другое обещание (ioWriteResult),


andSomethingElsePromise(),


.затем(списокРезультатов => ...)


  • Мы можем вручную инициировать и вернуть новый объект Promise, чтобы сформировать цепочку обещаний. Вместо того, чтобы выполнять задачи в одном обратном вызове, вы можете применить эту технику для сегментации кодовой базы на более мелкие фрагменты.

```javascript


первое обещание


.тог(() => {


const isSuccess = synchronousOperation() // логическое значение


вернуть успех? Promise.resolve("Успех") : Promise.reject(новая ошибка("404"))


.then((результат) => console.log(результат)) // Печатаем "Успех"


.catch(error) => console.error(error)) // Вывести ошибку с сообщением «404»


  • Метод then имеет второй и необязательный аргумент onRejectedCallback, но поскольку мы его не используем, всякий раз, когда возникает исключение, браузер просматривает всю цепочку промисов, чтобы найти первый приемлемый catch для данного ошибка.

Используя цепочку промисов, вы можете иметь одно внешнее «всеобъемлющее» решение, такое как try-catch, поэтому больше нет возможности проглотить ошибку. У вас может быть условный оператор в одном catch для нескольких ошибок, или вы можете разделить их на несколько отдельных catch, как показано ниже.


```javascript


отклонено5xxPromise


.catch(HTTP 4xx) // Браузер: "Не здесь"


.catch(HTTP 5xx) // Браузер: "Хорошо, это перехватывает ошибку 5xx"


.catch(Другие непредвиденные ошибки) // Пропустить


  • Вы можете связать then после catch. Это означает «всегда действовать, несмотря ни на что».

```javascript


отклонено5xxPromise


.поймать (HTTP 5xx)


.then(console.log("Эта строка всегда выводится"))


Заворачивать


Цепочка обещаний JavaScript — это простая, но мощная функция для решения распространенной проблемы с вложенными обратными вызовами (ад обратных вызовов). Чтобы связать обещания, нужно помнить два основных момента.


  1. Несколько последовательностей then и catch могут быть вызваны последовательно, например, promise.then(...).then(...).catch(...).catch(...).

  1. Обратный вызов в методе then должен возвращать новый объект Promise, чтобы можно было продолжить цепочку.

Это правило также относится к TypeScript. В ES2016 (он же ES7) была введена функция async/await, которая делает нашу жизнь еще проще. Тем не менее, если вы по какой-то причине не можете использовать функцию ES7, то цепочка промисов — отличный выбор для рефакторинга вашей кодовой базы.


Первоначально опубликовано в [JavaScript Promise Chaining — Избегайте адского обратного вызова] (https://hungvu.tech/javascript-promise-chaining-avoid-callback-hell).


Заинтересованы в веб-разработке? Другие мои статьи могут быть вам полезны!





Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE