Примечание. В этой статье предполагается, что у вас есть базовые знания JavaScript и Promises.

Теперь You Node #1 можно найти здесь

Недавно я узнал больше о новых изменениях в ES6+, и это дополнение может быть моим любимым в JavaScript. Мы все были частью Callback Hell или пытались понять, как правильно связать промисы, некоторые с параметрами, а некоторые без, но с помощью async/await мы можем упростить весь этот мусор и создать намного более чистый код. Я собираюсь пройти через некоторые ситуации, которые мы все видели, и как преобразовать их в асинхронный/ожидающий, чтобы помешать вам рвать на себе волосы!

Базовый случай

Это то место, где мы всегда были: копирование и вставка блоков .then() и .catch() снова, и снова, и снова.

function doThisCode() {
   someCallThatReturnsPromise().then(() => {
      // oh yay it's done
   }).catch((err) => {
      // oh no something went wrong
   });
}

К счастью, мы можем просто использовать эту логику и сохранить весь код на верхнем уровне, а не вкладывать все подряд. Представляем новый способ написания этого кода:

async function doThisCode() {
   const result = await someCallThatReturnsPromise().catch((err) => {
       // oh no something went wrong
   });
   
   // oh yay it's done
}

«Подождите… что только что произошло? Как я….где я…что?

^^^^^ именно мои первые мысли при виде этого ^^^^^

Хорошо, позвольте мне объяснить. Когда вы объявляете функцию как async (как мы сделали выше), она автоматически (настоящее слово программирования) возвращает промис для этой функции. Мечта сбылась? Довольно много. Итак, вы можете сделать это:

async function gimmeFive() { return 5; } 

И это вернет обещание, которое разрешается до значения 5. Сумасшествие, верно? Теперь вернемся к базовому случаю! Итак, мы очистили .then(), но .catch() все еще там. Однако важно то, что теперь, чтобы сделать что-то еще, нам не нужно вкладываться внутрь .then() или связывать их вместе. Мы разрешаем промис на верхнем уровне и ловим промис на верхнем уровне. Это значительно упрощает читаемость кода. Итак, теперь вы спрашиваете "А как насчет await?"

Чтобы использовать await, ваша функция должна быть объявлена ​​с async, чтобы сделать функцию асинхронной. Что тогда делает await? Он просто ждет разрешения промиса и возвращает результат (или выдает ошибку), пока не перейдет к выполнению следующих строк кода. Скажем, мы хотим сделать еще один асинхронный вызов после первого. С промисами это будет выглядеть примерно так:

someCallThatReturnsPromise()
   .then(secondPromiseCall)
   .then(thirdPromiseCall)
   .catch((err) => {
      // some error happened somewhere in those calls
   });

Но с асинхронным/ожиданием:

async function doThisCode() {
   const result = await someCallThatReturnsPromise().catch((err) => {
       // oh no something went wrong
   });
   
   const secondResult = await secondPromiseCall().catch((err) => {
      // the second call had an error
   });
}

Видите, как приятно поддерживать все на высшем уровне и иметь возможность обнаруживать отдельные ошибки, когда они случаются?

Совет: если у вас нет дескриптора .catch() для ваших вызовов с await, родительская функция выдаст ошибку, поэтому убедитесь, что вы их где-то ловите!

Я читаю ваши мысли, поэтому, естественно, ваш следующий вопрос: "А как насчет моих функций с обратным вызовом? Как они вписываются в async/await?»

Это правда, существует множество библиотек, использующих обратные вызовы. Черт возьми, даже многие встроенные функции NodeJS имеют обратные вызовы, поэтому нам нужно придумать какое-то решение, чтобы спасти себя от ада обратных вызовов. До сих пор мое решение заключалось в том, чтобы изолировать эти вызовы и обернуть их в промис, что затем дает вам возможность использовать async/await. Допустим, мы читаем файл, используя пакет NodeJS fs

fs.readFile('myFile.json', (err, data) => {
   // if err, do error
   // else do something with the data
});

Невозможно использовать это с async/await, но если мы обернем его в функцию, которая возвращает промис, тогда мы сможем использовать магию.

function readMyFile() {
   return new Promise((resolve, reject) => {
      fs.readFile('myFile.json', (err, data) => {
         if(err) { reject(err) }
         resolve(data)
      });
   });
}
async function loadAndSendFile() {
   const fileContents = await readMyFile().catch((err) => {
      // oh no the file read didn't work...
   });
   
   const wasSent = sendToSomeone(fileContents).catch((err) => {
      // some error happened trying to send
   });
   return wasSent;
}

Я вижу несколько преимуществ от включения этого кода обратного вызова в промис. Во-первых, конечно, вы можете использовать с ним async/await. Но во-вторых, и это более важно, это делает ваш код более читабельным с меньшими функциями и более легким для отладки. Конечно, вам придется написать еще несколько строк, но преимущества намного перевешивают усилия!

И это все на данный момент! Если у вас есть какие-либо вопросы, не стесняйтесь обращаться к ним, всегда приятно иметь возможность помочь другим разработчикам.

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