분류 javascript

JavaScript에서 비동기 대기 오류 처리

컨텐츠 정보

  • 조회 164 (작성일 )

본문

async/await에서의 오류 처리는 많은 혼란을 야기합니다. 비동기 함수에서 오류를 처리하는 데는 여러 가지 패턴이 있으며 숙련된 개발자조차도 때때로 오류가 발생합니다.


비동기 함수 run()이 있다고 가정하십시오. 이 기사에서는 run()에서 오류를 처리하기 위한 3 가지 패턴 (try / catch, Golang-style 및 catch())을 함수 호출에서 설명합니다. 또한 비동기 함수를 사용하여 catch() 이외의 것을 거의 필요로 하지 않는 이유에 대해서도 설명합니다.


http://thecodebarbarian.com/async-await-error-handling-in-javascript.html 


try/catch 


async / await를 처음 시작하면 모든 비동기 작업 주위에 try / catch를 사용하고 싶은 유혹이 있습니다. 거부하는 promise을 await하면 JavaScript에서 잡을 수 있는 오류가 발생하기 때문입니다.


run();

async function run() {
  try {
    await Promise.reject(new Error('Oops!'));
  } catch (error) {
    error.message; // "Oops!"
  }
}

try / catch는 동기 오류도 처리합니다.


run();

async function run() {
  const v = null;
  try {
    await Promise.resolve('foo');
    v.thisWillThrow;
  } catch (error) {
    // "TypeError: Cannot read property 'thisWillThrow' of null"
    error.message;
  }
}

그래서 당신이 해야 할 일은 모든 논리를 try / catch로 감싸는 것입니다. 좀 빠지는. 아래 코드는 처리되지 않은 약속 거부로 이어집니다. await 키워드는 약속 거부를 catchable 오류로 변환하지만 return은 그렇지 않습니다.


run();

async function run() {
  try {
    // Note that this is a `return`, not `await`
    return Promise.reject(new Error('Oops!'));
  } catch (error) {
    // Will **not** run
  }
}

return await를 사용하여 이 제한을 해결할 수 있습니다. 그러나 return await를 잊어 버리기 쉽습니다.


다른 단점은 try / catch가 작성하기 어렵다는 것입니다. try / catch가 동기화 및 비동기 오류를 처리한다는 것을 알고 나면 아래에 표시된 것처럼 모든 async 논리를 한 번의 try/catch로 래핑하고 싶은 유혹이 있습니다.


Golang in JS 


또 다른 일반적인 패턴은 .then()을 사용하여 거부 된 promise을 오류가 있는 promise으로 변환하는 것입니다. 그런 다음 Golang과 같이 if(err) 검사를 사용할 수 있습니다.


run();

async function throwAnError() {
  throw new Error('Oops!');
}

async function noError() {
  return 42;
}

async function run() {
  // The `.then(() => null, err => err)` pattern gives you an
  // error if one occurred, or `null` otherwise
  let err = await throwAnError().then(() => null, err => err);
  if (err != null) {
    err.message; // 'Oops'
  }

  err = await noError().then(() => null, err => err);
  err; // null
}

오류와 값이 모두 필요한 경우 실제로 JavaScript로 Golang을 작성하는 것처럼 가장 할 수 있습니다.


run();

async function throwAnError() {
  throw new Error('Oops!');
}

async function noError() {
  return 42;
}

async function run() {
  // The `.then(v => [null, v], err => [err, null])` pattern
  // lets you use array destructuring to get both the error and
  // the result
  let [err, res] = await throwAnError().
    then(v => [null, v], err => [err, null]);
  if (err != null) {
    err.message; // 'Oops'
  }

  err = await noError().
    then(v => [null, v], err => [err, null]);
  err; // null
  res; // 42
}

try 블록에서 변수를 선언하여 변수를 try 블록으로 범위를 지정하기 때문에 이 패턴은 구문 적으로 깔끔 할 수 있습니다.


const getAnswer = async () => 42;

run();

async function run() {
  try {
    let val = await getAnswer();
  } catch (error) {}

  // ReferenceError: val is not defined
  val;
}

Golang 스타일 오류 처리는 반환 문제를 제거하지 않습니다. 비동기 작업 후 if (err! = null)이 없으면 무언가 잘못되었음을 알기 때문에 누락 된 오류 검사를 더 어렵게 만듭니다.


Golang 스타일 오류 처리에는 두 가지 주요 단점이 있습니다.

  1. 매우 반복적입니다. 비동기식으로 할 때마다 if (err! = null)를 입력하면 손목 터널로 가는 급행 차선으로 연결됩니다.
  2. run()의 동기 오류에 도움이 되지 않습니다.

따라서 Golang 스타일의 오류 처리는 깔끔한 구문 단축키로 드물게 사용해야 합니다. try / catch를 사용하는 것보다 많은 이점이 없습니다.


함수 호출에서 catch() 사용 


try/catch 및 Golang 스타일 오류 처리는 모두 용도가 있지만 run() 함수의 모든 오류를 처리하는 가장 좋은 방법은 run(). catch()를 사용하는 것입니다. 즉, 각 개별 오류를 처리하는 대신 함수를 호출 할 때 오류를 처리하십시오.


run().
  catch(function handleError(err) {
    err.message; // Oops!
  }).
  // Handle any errors in `handleError()`. If the error handler
  // throws an error, kill the process.
  catch(err => { process.nextTick(() => { throw err; }) });

async function run() {
  await Promise.reject(new Error('Oops!'));
}

비동기 함수는 항상 약속을 반환합니다. 이 약속은 함수에서 포착되지 않은 오류가 발생하면 거부합니다. 비동기 함수 본문이 거부하는 약속을 반환하면 반환 된 약속도 거부됩니다.


run().
  catch(function handleError(err) {
    err.message; // Oops!
  }).
  // Handle any errors in `handleError()`. If the error handler
  // throws an error, kill the process.
  catch(err => { process.nextTick(() => { throw err; }) });

async function run() {
  // Note that this is `return`, not `await`
  return Promise.reject(new Error('Oops!'));
}

try/catch에 전체 run() 함수 본문을 래핑하는 것과 달리 run(). catch()가 필요한 이유는 무엇입니까? 오류 처리기에서 오류를 처리합니다. try/catch의 catch 블록에 오류가 발생하면 어떻게 됩니까? 유일한 해결책은 모든 단일 함수에서 catch 블록에 try/catch를 중첩시키는 것입니다. .catch()는 오류 처리기 클리너에서 예기치 않은 오류를 처리합니다.


Takeaways 


일반적으로 오류는 예상되거나 예상치 못한 오류입니다. 비동기 함수에서 try/catch를 사용하면 예상되는 오류를 정상적으로 복구 할 수 있습니다. 그러나 예기치 않은 오류가 발생하면 때때로 "TypeError : null 속성 'foo'속성을 읽을 수 없습니다"라는 놀라운 결과가 발생합니다.


호출 함수의 비동기 함수에서 예기치 않은 오류를 처리해야 합니다. run() 함수는 모든 가능한 오류를 처리하는 책임을 지지 않아야 하며 대신 run().catch (handleError)를 수행해야 합니다.