async / await로 try / catch 블록


116

나는 노드 7 async / await 기능을 파헤 치고 다음과 같은 코드를 가로 질러 계속 걸었다.

function getQuote() {
  let quote = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
  return quote;
}

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch (error) {
    console.error(error);
  }
}

main();

이것이 async / await를 사용하여 해결 / 거부 또는 반환 / 던지기를 할 수있는 유일한 가능성 인 것처럼 보이지만 v8은 try / catch 블록 내에서 코드를 최적화하지 않습니다.

대안이 있습니까?


'대기 후 던지기가 성공하지 못함'은 무엇을 의미합니까? 오류가 발생하면? 예상 한 결과를 반환하지 않으면? 캐치 블록에서 다시 던질 수 있습니다.
DevDig

AFAIK V8 최적화 시도 / 캐치를 수행 throw 문은 느린 하나입니다
타마스 Hegedus

1
나는 여전히 질문을 이해하지 못한다. 당신은 오래된 약속 체인을 사용하지만 더 빠를 것이라고 생각하지 않습니다. 그렇다면 try-catch의 성능에 대해 걱정하십니까? 그러면 async await로 무엇을 할 수 있습니까?
Tamas Hegedus 2011

내 대답을 확인 나는 깨끗한 접근을 얻기 위해 노력
zardilior

여기에서 할 수 있습니다. stackoverflow.com/a/61833084/6482248 깨끗해 보입니다
Prathamesh More

답변:


133

대안

이에 대한 대안 :

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch (error) {
    console.error(error);
  }
}

약속을 명시 적으로 사용하면 다음과 같습니다.

function main() {
  getQuote().then((quote) => {
    console.log(quote);
  }).catch((error) => {
    console.error(error);
  });
}

또는 다음과 같이 연속 전달 스타일을 사용합니다.

function main() {
  getQuote((error, quote) => {
    if (error) {
      console.error(error);
    } else {
      console.log(quote);
    }
  });
}

원래 예

원래 코드가하는 일은 실행을 일시 중단하고에서 반환 된 약속 getQuote()이 해결 될 때까지 기다리는 것입니다 . 그런 다음 실행을 계속하고 반환 된 값을 var quote기록한 다음 약속이 해결 된 경우이를 인쇄하거나 예외를 throw하고 약속이 거부 된 경우 오류를 인쇄하는 catch 블록을 실행합니다.

두 번째 예제와 같이 Promise API를 사용하여 동일한 작업을 직접 수행 할 수 있습니다.

공연

이제 성능을 위해. 테스트 해 봅시다!

난 그냥이 코드를 작성 - f1()제공 1, 반환 값으로 f2()던져 1예외로 :

function f1() {
  return 1;
}

function f2() {
  throw 1;
}

이제 동일한 코드를 백만 번 호출 해 보겠습니다 f1().

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f1();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);

그리고 다음의 변화하자 f1()에를 f2():

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f2();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);

이것은 내가 얻은 결과입니다 f1.

$ time node throw-test.js 
1000000

real    0m0.073s
user    0m0.070s
sys     0m0.004s

이것이 내가 얻은 것입니다 f2.

$ time node throw-test.js 
1000000

real    0m0.632s
user    0m0.629s
sys     0m0.004s

하나의 단일 스레드 프로세스에서 초당 2 백만 번의 작업을 수행 할 수있는 것 같습니다. 그 이상을하고 있다면 그것에 대해 걱정해야 할 수도 있습니다.

요약

Node.js에서는 그런 것에 대해 걱정하지 않습니다. 그런 것들이 많이 사용되면 결국 V8이나 SpiderMonkey 또는 Chakra 팀에 의해 최적화되고 모든 사람들이 따를 것입니다. 원칙적으로 최적화되지 않은 것 같지 않습니다. 문제가 아닙니다.

최적화되지 않은 경우에도 Node에서 CPU를 최대로 사용한다면 C로 숫자 크 런칭을 작성해야한다고 주장합니다. 그것이 기본 애드온의 목적입니다. 또는 node.native 와 같은 것이 Node.js보다 작업에 더 적합 할 수 있습니다.

너무 많은 예외를 던질 필요가있는 사용 사례가 무엇인지 궁금합니다. 일반적으로 값을 반환하는 대신 예외를 던지는 것은 예외입니다.


나는 코드가 Promises로 쉽게 작성 될 수 있다는 것을 알고 있습니다. 언급했듯이 다양한 예제에서 그것을 보았 기 때문에 제가 묻고 있습니다. try / catch 내에서 단일 작업을하는 것은 문제가되지 않을 수 있지만 추가 응용 프로그램 논리가있는 여러 async / await 함수는 문제가 될 수 있습니다.
Patrick

4
@Patrick "might be"와 "will be"는 추측과 실제 테스트의 차이입니다. 그것이 귀하의 질문에 있었기 때문에 단일 문에 대해 테스트했지만 내 예제를 쉽게 변환하여 여러 문을 테스트 할 수 있습니다. 또한 요청한 비동기 코드를 작성하기위한 몇 가지 다른 옵션도 제공했습니다. 귀하의 질문에 대한 답변이 있으면 답변 수락을 고려할 수 있습니다 . 요약하자면, 물론 예외는 반환보다 느리지 만 사용은 예외 여야합니다.
rsp

1
예외를 던지는 것은 실제로 예외입니다. 즉, 예외 발생 여부에 관계없이 코드는 최적화되지 않습니다. 성능 저하 try catch는 예외 발생이 아니라를 사용 하여 발생합니다. 숫자는 적지 만 테스트에 따르면 거의 10 배 느리지 만 중요하지 않습니다.
Nepoxx

22

Golang의 오류 처리와 유사한 대안

async / await는 내부적으로 promise를 사용하기 때문에 다음과 같은 약간의 유틸리티 함수를 작성할 수 있습니다.

export function catchEm(promise) {
  return promise.then(data => [null, data])
    .catch(err => [err]);
}

그런 다음 오류를 포착해야 할 때마다 가져오고 약속을 반환하는 비동기 함수를 래핑하십시오.

import catchEm from 'utility';

async performAsyncWork() {
  const [err, data] = await catchEm(asyncFunction(arg1, arg2));
  if (err) {
    // handle errors
  } else {
    // use data
  }
}

- 나는 정확히 수행하는 위의 NPM 패키지 생성 npmjs.com/package/@simmo/task
마이크

2
: 정확히 수행하는 인기 패키지가 이미있다 - 당신은 다시 발명 휠을 수 있습니다 @ 마이크 npmjs.com/package/await-to-js
야쿱 Kukul

21

try-catch 블록의 대안은 await-to-js lib입니다. 자주 사용합니다. 예를 들면 :

import to from 'await-to-js';

async function main(callback) {
    const [err,quote] = await to(getQuote());
    
    if(err || !quote) return callback(new Error('No Quote found'));

    callback(null,quote);

}

이 구문은 try-catch와 비교할 때 훨씬 더 깔끔합니다.


이것을 시도하고 그것을 좋아했습니다. 새 모듈을 설치하는 대신 깔끔하고 읽기 쉬운 코드. 그러나 많은 비동기 함수를 작성할 계획이라면 이것이 훌륭한 추가라고 말해야합니다! 감사합니다
filipbarak

15
async function main() {
  var getQuoteError
  var quote = await getQuote().catch(err => { getQuoteError = err }

  if (getQuoteError) return console.error(err)

  console.log(quote)
}

또는 가능한 var를 선언하는 대신 상단에 오류를 보유 할 수 있습니다.

if (quote instanceof Error) {
  // ...
}

TypeError 또는 Reference 오류와 같은 오류가 발생하면 작동하지 않습니다. 그래도 일반 오류인지 확인할 수 있습니다.

async function main() {
  var quote = await getQuote().catch(err => {
    console.error(err)      

    return new Error('Error getting quote')
  })

  if (quote instanceOf Error) return quote // get out of here or do whatever

  console.log(quote)
}

내가 선호하는 것은 모든 것을 큰 try-catch 블록에 래핑하는 것인데, 여기에서 여러 약속이 생성되어 오류를 생성 한 약속에 대한 오류를 처리하는 것이 번거로울 수 있습니다. 대안은 똑같이 성가신 여러 try-catch 블록입니다.


8

더 깨끗한 대안은 다음과 같습니다.

모든 비동기 기능이 기술적으로 약속이라는 사실 때문에

await로 호출 할 때 함수에 catch를 추가 할 수 있습니다.

async function a(){
    let error;

    // log the error on the parent
    await b().catch((err)=>console.log('b.failed'))

    // change an error variable
    await c().catch((err)=>{error=true; console.log(err)})

    // return whatever you want
    return error ? d() : null;
}
a().catch(()=>console.log('main program failed'))

모든 promise 오류가 처리되고 코드 오류가 없으므로 try catch가 필요하지 않습니다. 부모에서이를 생략 할 수 있습니다 !!

mongodb로 작업하고 있다고 가정 해 보겠습니다. 오류가 있으면 래퍼를 만들거나 try catch를 사용하는 것보다 호출하는 함수에서 처리하는 것이 더 좋습니다.


3 가지 기능이 있습니다. 하나는 값을 얻고 오류를 포착하고, 다른 하나는 오류가 없으면 반환하고 마지막으로 콜백을 사용하여 첫 번째 함수를 호출하여 오류를 반환했는지 확인합니다. 이 모든 것은 단일 "promise".then (cb) .catch (cb) 또는 trycatch 블록에 의해 해결됩니다.
고시 족장

@Chiefkoshi 보시다시피 오류가 세 가지 경우 모두 다르게 처리되므로 단일 캐치가 수행되지 않습니다. 첫 번째가 실패하면 d ()를 반환하고 두 번째가 실패하면 마지막이 실패하면 null을 반환하고 다른 오류 메시지가 표시됩니다. 질문은 await를 사용할 때 오류 처리를 요청합니다. 그래서 그것도 답입니다. 하나라도 실패하면 모두 실행해야합니다. Try catch 블록은이 특정 예제에서 3
개가

1
이 질문은 약속 실패 후 실행을 요구하지 않습니다. 여기서 B를 기다린 다음 C를 실행하고 오류가 발생하면 D를 반환합니다. 이건 어때요? C는 B를 기다려야하지만 서로 독립적입니다. 나는 그들이 독립한다면 왜 A에 함께 있을지에 대한 이유를 알지 못합니다. 그들이 서로 의존하는 경우 B가 실패하면 C의 실행을 중지하고 싶을 것입니다. .then.catch 또는 try-catch의 작업. 나는 그들이 아무것도 반환하지 않고 A와 완전히 관련이없는 일부 비동기 작업을 수행한다고 가정합니다. 왜 async await로 호출됩니까?
고시 족장

질문은 async / await를 사용할 때 오류를 처리하기 위해 catch 블록을 시도하는 대안과 관련이 있습니다. 여기에있는 예는 설명을위한 것이며 예일뿐입니다. 일반적으로 async / await가 사용되는 방식 인 순차적 인 방식으로 독립적 인 작업을 개별적으로 처리하는 방법을 보여줍니다. 비동기 대기로 호출되는 이유는 처리 방법을 보여주는 것입니다. 정당한 것 이상을 설명합니다.
zardilior

2

나는 이렇게하고 싶다 :)

const sthError = () => Promise.reject('sth error');

const test = opts => {
  return (async () => {

    // do sth
    await sthError();
    return 'ok';

  })().catch(err => {
    console.error(err); // error will be catched there 
  });
};

test().then(ret => {
  console.log(ret);
});

오류를 처리하는 것과 유사합니다. co

const test = opts => {
  return co(function*() {

    // do sth
    yield sthError();
    return 'ok';

  }).catch(err => {
    console.error(err);
  });
};

코드는 명확하지 않지만 재미있어 보이지만 편집 할 수 있습니까?
zardilior

이 답변에 설명이 없다는 것은 불행히도 실제로 할당 한 모든 const를 try-catch하는 것을 피하는 좋은 방법을 보여주기 때문입니다 await!
Jim

0

catch내 경험상 이런 식으로하는 것은 위험합니다. 이 약속의 오류뿐만 아니라 전체 스택에서 발생한 모든 오류가 포착됩니다 (원하는 것이 아닐 수도 있음).

약속에 대한 두 번째 인수는 이미 거부 / 실패 콜백입니다. 대신 사용하는 것이 더 좋고 안전합니다.

다음은이를 처리하기 위해 작성한 typescript typesafe 한 줄입니다.

function wait<R, E>(promise: Promise<R>): [R | null, E | null] {
  return (promise.then((data: R) => [data, null], (err: E) => [null, err]) as any) as [R, E];
}

// Usage
const [currUser, currUserError] = await wait<GetCurrentUser_user, GetCurrentUser_errors>(
  apiClient.getCurrentUser()
);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.