Почему исключение все еще генерируется после catch в асинхронной функции?

#javascript #node.js #error-handling #async-await

#javascript #node.js #обработка ошибок #async-await

Вопрос:

Когда исключение поймано, остальная часть кода все еще выполняется.

 function bad(){
    //throw new Error('I am an exception')
    return Promise.reject("I am an exception")
}

(
 async function (){
      let  msg = await bad().catch( e=>console.log("This is an error: "   e))
      console.log("I want to stop before executing this line ")
 }
)()
  

Я пытаюсь поймать исключение таким образом, вместо того, чтобы пытаться поймать

Моя первая проблема заключается в том, как остановить доступ кода к остальной части кода после обнаружения ошибки.

Вторая проблема заключается в том, что если я заменю отклонение обещания на закомментированную ошибку throw, выданное исключение не будет перехвачено, почему это?

Ответ №1:

Потому что вы преобразуете отклонение в выполнение с помощью вашего catch обработчика. await Будет ожидать обещания, возвращенного catch , которое никогда не отклоняется. Таким образом, нет причин, по которым код не продолжался бы, поскольку await видит только выполненное (никогда не отклоненное) обещание.

Помните, что catch возвращает новое обещание, которое будет выполнено на основе того, что делает обработчик отклонения. Ваш обработчик отклонения подавляет ошибку, эффективно возвращая undefined , таким образом выполняя обещание.

Это очень похоже на то, как если бы вы сделали это:

 async function (){
    let msg;
    try {
        msg = await bad();
    } catch (e) {
        console.log("This is an error: "   e);
    }
    console.log("I want to stop before executing this line ")
}
  

В комментарии, который вы спросили:

Итак, нет другого способа, кроме как включить весь мой код в блок try и использовать только однострочный .catch() , если у меня больше нет кода для выполнения?

Просто чтобы убрать это с пути: обычно лучше всего вообще не обрабатывать ошибки ни в какой функции, кроме точки входа верхнего уровня в код (например, функции, вызываемые на верхнем уровне вашего кода или обработчиками событий). Обработка ошибок слишком рано — это классический антипаттерн. Разрешить им распространяться на вызывающую программу, где они могут обрабатываться централизованным образом.

Но в ситуациях, когда уместно обрабатывать ошибку на этом уровне, да, try / catch , вероятно, ваш лучший выбор со следующей логикой в try блоке:

 async function() {
     try {
         let msg = await bad();
         console.log("I want to stop before executing this line ")
     } catch (e) {
         console.log("This is an error: "   e);
     }
}
  

Его преимущество также заключается в том, что в содержащей области видимости не объявлена переменная, которую вы не собираетесь использовать. Если для вас важна «однострочная» функция, то между .catch и catch вообще нет большой разницы:

 async function() {
     try {
         let msg = await bad();
         console.log("I want to stop before executing this line ")
     } catch (e) { console.log("This is an error: "   e); }
}
  

Или вы могли бы вернуть какое-либо значение флага:

 async function() {
     let msg = await bad().catch(e => { console.log("This is an error: "   e); return null; });
     if (msg === null) {
         return;
     }
     console.log("I want to stop before executing this line ")
}
  

Но, честно говоря, это действительно плывет против течения. 🙂

Комментарии:

1. @YoussefMohamed — Я обновил ответ, чтобы учесть ваш первый комментарий. Боюсь, я не понимаю, о чем вы спрашиваете во втором.

2. @YoussefMohamed — Ты имеешь в виду function bad(){ throw new Error('I am an exception'); } ? Поскольку bad это не async функция, то это синхронная ошибка, вызванная вызовом bad , которая заставляет async функцию, из которой вы ее вызываете, отклонять свое обещание. Оно не переходит к catch потому что bad() завершается ошибкой. (Даже если бы это было не так, оно не вернуло бы обещание.) Если бы bad это была async функция, она throw действительно была бы перехвачена .catch обработчиком.

3. (Просто примечание: я подробно рассматриваю promises в главе 8 моей новой книги и async / await в главе 9. Ссылки в моем профиле, если вам интересно.)

4. @YoussefMohamed — Верно, это та же ситуация, что и выше — bad() часть генерирует синхронное исключение, поэтому она не возвращает обещание, поэтому нет обещания вызвать .catch метод, так что … 🙂

5. Просто чтобы немного добавить: Полезно помнить, что .then / .catch не является специальным синтаксисом, это просто вызовы метода для объекта, возвращаемого функцией, которая возвращает обещание. Напротив, внутри async функции поведение try / catch , throw и даже циклов (если в цикле есть await ) изменены для поддержки асинхронного выполнения.