JavaScript async / await

kimjh96·2021년 8월 11일

ES8(ECMAScript2017)의 공식 스펙으로 비교적 최근에 정의된 비동기 처리 문법이다. 기존의 비동기 처리 방식인 콜백화 Promise 의 단점이 보완하여 개발자가 읽기 좋은 코드를 작성할 수 있게끔 해준다.

기존의 Promise 를 활용한 비동기 처리

function fetchAuthorName(postId) {
  return fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`)
    .then((response) => response.json())
    .then((post) => post.userId)
    .then((userId) => {
      return fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
        .then((response) => response.json())
        .then((user) => user.name);
    });
}

fetchAuthorName(1).then((name) => console.log("name:", name));

브라우저의 내장 함수인 fetch() 를 호출하여 Promise 객체를 리턴받은 후에 메소드 체이닝 기법을 통해 then() 메소드를 연쇄적으로 호출하고 있다. 그리고 fetchAuthorName() 함수 자체도 결국 게시물 작성자의 이름을 얻을 수 있는 Promise 객체를 리턴하기 때문에, then() 메소드를 사용하여 게시물 작성자의 이름을 출력한다.

이렇게 Promise 를 활용하여 비동기 처리를 하는 경우 여러가지 문제점이 발생하게 된다.

디버깅

위 코드의 8번째 줄을 의도적으로 에러가 발생하도록 수정한 뒤 실행해보면 아래와 같은 에러 메시지를 보게 된다.

.then(user => user1.name);

// ReferenceError: user1 is not defined
// at fetch.then.then.then.then.then...

동일한 이름의 메소드인 then() 을 연쇄적으로 호출하고 있어서 몇번째 then() 에서 문제가 발생한건지 Stack Trace 를 보아도 혼란스러울 수 있다. 또한 break point 를 걸고 디버거를 돌리면, 위의 코드와 같이 화살표 함수로 한 줄짜리 콜백 함수를 넘긴 경우에는 코드 실행이 break point 에서 멈추지 않기 때문에 디버깅이 상당히 불편하다.

예외 처리

Promise 는 try/catch 대신 catch() 메소드를 사용하여 예외 처리를 해야한다. 이 부분이 비동기 코드만 있을 때는 그렇게 거슬리지 않지만, 동기 코드와 비동기 코드가 섞여 있을 경우에는 예외 처리가 난해해지거나 누락되는 경우가 발생하기 쉽다.

가독성

then() 메소드의 인자로 넘기는 콜백 함수 내에서 조건문이나 반복문을 사용하거나 여러 개의 Promise 를 병렬 또는 중첩해서 호출하는 경우 다단계 들여쓰기를 해야할 확률이 높아지며 코드의 가독성은 점점 떨어지게 된다.

async/await 을 활용한 비동기 처리

위의 Promise 를 활용한 비동기 처리 코드를 아래와 같이 작성할 수 있다.

async function fetchAuthorName(postId) {
  const postResponse = await fetch(
    `https://jsonplaceholder.typicode.com/posts/${postId}`
  );
  const post = await postResponse.json();
  const userId = post.userId;
  const userResponse = await fetch(
    `https://jsonplaceholder.typicode.com/users/${userId}`
  );
  const user = await userResponse.json();
  return user.name;
}

fetchAuthorName(1).then((name) => console.log("name:", name));

await 키워드는 async 키워드가 붙어있는 함수 내에서만 사용할 수 있으며 비동기 함수가 리턴하는 Promise 로 부터 결과 값을 추출해준다. 즉, await 키워드를 사용하면 일반 비동기 처리처럼 바로 실행이 다음 라인으로 넘어가는 것이 아니라 결과 값을 얻을 수 있을 때까지 기다려준다.

일반적인 동기 코드처리와 동일한 흐름으로 코드를 작성할 수 있기 때문에 코드 읽기가 한결 수월해진다.

aysnc 키워드가 붙어있는 함수를 호출하면 명시적으로 Promise 객체를 return 하지 않아도 Promise 객체가 return 된다. 호출부를 보면 Promise 객체를 사용했던 것과 동일한 방식으로 then() 메소드를 통해 결과 값을 출력하고 있다.

예외 처리 또한 동기/비동기 구분 없이 try/catch 로 일관되게 처리할 수 있다.

Reference
https://www.daleseo.com/js-async-async-await/
https://joshua1988.github.io/web-development/javascript/js-async-await/

0개의 댓글