[node.js] async / await로 try / catch 블록

나는 노드 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 블록 내에서 코드를 최적화하지 않습니다.

대안이 있습니까?



답변

대안

이에 대한 대안 :

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보다 작업에 더 적합 할 수 있습니다.

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


답변

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
  }
}


답변

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와 비교할 때 훨씬 더 깔끔합니다.


답변

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 블록입니다.


답변

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

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

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를 사용하는 것보다 호출하는 함수에서 처리하는 것이 더 좋습니다.


답변

나는 이렇게하고 싶다 🙂

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);
  });
};


답변

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()
);