비동기 예외처리 - 콜백함수, promise, async/await

GY·2022년 3월 21일
0

[JS] 개념 정리

목록 보기
32/32
post-thumbnail
post-custom-banner

ES8부터 도입된async, await은 보다 가독성 좋게 비동기 처리를 동기 처리처럼 동작하도록 구현할 수 있는 문법이다.

프로미스를 기반으로 동작하는데, then, catch, finally 의 후속 처리 메서드 없이도 프로미스가 동기처리처럼 처리결과를 반환하도록 구현할 수 있다.

async

async 키워드를 사용한 함수는 언제나 프로미스를 반환한다.
명시적으로 프로미스를 반환하지 않더라도 async 함수는 암묵적으로 반환값을 resolve하는 프로미스를 반환한다.

await

await 키워드는 프로미스가 settled 상태가 될 때까지 대기한다.

settled : 비동기 처리가 수행된 상태

그리고 settled 상태가되면 프로미스가 resolve한 처리결과를 반환한다.

예를 들어보자.

async function fetchData() {
	const res = await fetch('url')

await 키워드는 fetch 함수가 요청한 응답이 도착해서, fetch 함수가 반환한 promise가 settled 상태가 될 때까지 대기한다.
이후 settled 상태가 되면 프로미스가 resolve한 처리결과가 res 변수에 할당된다.

즉, await 키워드는 실행문을 잠깐 중지시켰다가 프로미스가 settled 상태가 되었을 때 다시 실행시킨다.

모든 프로미스에 await을 사용할 필요는 없다.

각각의 비동기 처리가 개별적으로 수행되어 순차적인 처리가 필요가 없다면, promise.all로 묶어서 await 키워드를 붙여주면 된다.

async function foo() {
  const res = await promise.all([
    new Promise(...
    new Promise(...

에러처리

콜백패턴은 에러처리에 한계가 있다.

비동기 처리를 위한 콜백 패턴은 에러처리에 한계가 있었다.
에러는 호출자 방향으로 전파된다.
즉 콜 스택의 아래방향인 실행 컨텍스트가 푸시되기 직전에 푸시된 실행 컨텍스트 방향으로 전파된다. 하지만 비동기 함수의 콜백함수를 호출한 것은 비동기 함수가 아니기 때문에 try catch 문을 사용해 에러로 캐치할 수 없다.

예시를 보며 이해해보자.

try {
	setTimeout(() => { throw new Error('Error!');}, 1000);
} catch (e) {
	console.error('캐치한 에러', e);
}
  1. setTimeout 함수가 호출되면 실행컨텍스트가 생성되어 콜 스택에 푸시되고, 실행된다.

이 때 비동기 함수이므로 콜백함수가 호출되는 것을 기다리지 않고 즉시 종료되어 콜 스택에서 제거된다.

  1. 이후 타이머가 만료되면 setTimeout 함수의 콜백함수는 태스크 큐로 푸시되고 콜 스택이 비었을 때 이벤트 루프에 의해 콜 스택으로 푸시되어 실행된다.

그런데 콜백함수가 실행될 때 setTimeout 함수는 이미 콜스택에서 제거되었으므로, 콜백함수를 호출한 것이 setTimeout 함수가 아니게 된다.

왜 호출자가 아니라는 거지?

콜백함수의 실행컨텍스트가 실행중일 때 하위에 setTimeout함수의 실행컨텍스트가 있어야 호출자라고 할 수 있다.

에러는 호출자 방향으로 전파된다.
즉, 콜 스택에 아래방향 = 실행중이 함수 직전에 푸시된 실행컨텍스트 방향으로 전파된다.
그런데 콜백함수의 호출자는 setTimeout함수가 아니게 되므로, setTimeout함수의 콜백함수가 발생시킨 에러는 catch 블록에서 캐치되지 않는다.

결국 비동기 처리를 위한 콜백 패턴은 콜백 헬 뿐 아니라 에러처리가 곤란하다는 문제가 있다.
이것을 극복하기 위해 프로미스가 도입되었다.


promise, async/await의 에러처리

async/await을 사용하면 promise의 후속처리메서드에 콜백함수를 전달해 비동기 처리결과를 후속처리할 필요가 없다.

마치 동기 처리처럼 프로미스를 사용할 수 있다.
다시 말해, 프로미스의 후속처리 메서드 없이 마치 동기 처리 처럼 프로미스가 처리 결과를 반환하도록 구현할 수 있다.

콜백 함수를 인수로 전달받는 비동기 함수와는 달리,
프로미스를 반환하는 비동기 함수는 명시적으로 호출할 수 있기 때문에 호출자가 명확하다.


Reference

  • 모던 자바스크립트 딥다이브
profile
Why?에서 시작해 How를 찾는 과정을 좋아합니다. 그 고민과 성장의 과정을 꾸준히 기록하고자 합니다.
post-custom-banner

0개의 댓글