[번역] await vs return vs return await

Jake Archibald의 await vs return vs return await 번역글입니다. 굳이 이 글이 아니더라도 모든 번역글은 역자의 의도와 상관없이 원문의 내용과 다르게 전달될 수 있으니 원문도 같이 보시는 걸 권해드립니다.

async 함수를 작성할 때 사용하는 await, return, return await의 차이점을 알고, 올바른 선택을 하는 것이 중요하다. 아래 예제를 보자.

async function waitAndMaybeReject() {
  // Wait one second
  await new Promise(r => setTimeout(r, 1000));

  // Toss a coin
  const isHeads = Boolean(Math.round(Math.random()));

  if (isHeads) return 'yay';
  throw Error('Boo!');
}

이 함수는 반반의 확률로 Fulfilled(이행) 상태가 되어 'yay'를 던져주거나 Rejected(실패) 상태가 되어 에러를 던지는 promise를 반환한다. 이걸 몇 가지 다른 방식으로 사용해보자.


Just calling

async function foo() {
  try {
    waitAndMaybeReject();
  }
  catch (e) {
    return 'caught';
  }
}

위와 같이 foo를 호출하면 항상 undefined인 Fulfilled 상태의 promise를 곧바로 리턴할 것이다. waitAndMaybeReject()를 await 하거나 리턴하지 않았기 때문이다.

Awaiting

async function foo() {
  try {
    await waitAndMaybeReject();
  }
  catch (e) {
    return 'caught';
  }
}

foo를 호출하면 이번엔 항상 1초를 기다린 뒤 undefined나 'caught'인 Fulfilled 상태의 promise를 리턴할 것이다. 위 코드에서 waitAndMaybeReject()를 await 하기 때문에 해당 함수가 Rejected 상태의 promise를 리턴하면 catch 블록이 실행되어 'caught'를 리턴하지만, Fulfilled 상태의 promise를 리턴하면 여전히 await만 할 뿐 아무 동작을 하지 않기 때문에 yay를 얻을 수 없다.

Returning

async function foo() {
  try {
    return waitAndMaybeReject();
  }
  catch (e) {
    return 'caught';
  }
}

foo를 호출하면 항상 1초를 기다린 뒤, 'yay'를 가진 Fulfilled 상태나 Error('Boo!')를 throw하는 Rejected 상태의 promise를 리턴할 것이다. waitAndMaybeReject()를 await 하지 않고 바로 리턴하기 때문에 catch 블록은 절대 실행되지 않는다.

Return-awaiting

async function foo() {
  try {
    return await waitAndMaybeReject();
  }
  catch (e) {
    return 'caught';
  }
}

foo를 호출하면 항상 1초를 기다린 뒤, 'yay'나 'caught'를 가진 Fulfilled 상태의 promise를 리턴할 것이다. waitAndMaybeReject()를 await 했기 때문에 해당 함수가 Rejected 상태의 promise를 리턴하면 catch 블록이 실행되어 'caught'를 리턴하고, Fulfilled 상태이면 'yay'를 리턴한다.


위의 내용들이 혼란스러우면 아래와 같이 두 단계로 생각하면 좀 더 쉬울 것이다.

async function foo() {
  try {
    // waitAndMaybeReject()의 결과가 정해질 때까지 기다린 후,
    // 그 값을 fulfilledValue에 할당
    const fulfilledValue = await waitAndMaybeReject();

    // 그리고 fulfilledValue를 리턴
    return fulfilledValue;
  }
  catch (e) {
    // 만약 waitAndMaybeReject()가 Rejected 상태면
    // catch 블록이 실행되기 때문에 'caught'를 리턴
    return 'caught';
  }
}

Note: async 함수의 리턴값은 항상 Promise.resolve로 wrapped 되어 있기 때문에 return await는 실효성이 없다. 다만 try/catch 구문에서는 다른 Promise 함수의 오류를 catch 하기 위해 사용될 수 있다.