210623. Today I Learned(TIL) : 비동기(Asynchronous) Promise

syong·2021년 6월 23일
0

TIL

목록 보기
16/32

비동기적 실행이란?

비동기적 실행은 동기적 실행의 반대 개념으로 일상생활에서 '동기적', '비동기적' 이라는 단어를 자주 접해보진 못했을 것이다. 먼저 동기적 실행은, 어떤 일에 요청이 들어오면 그 요청에 즉시 응답하는 응답처리 방식이다. 동기적 실행은 요청에 대한 응답을 즉시 돌려주기 때문에 응답을 돌려주기 전까지 다른 요청은 받을 수 없는 상태로 막혀있는(blocking) 상태가 되어버리기 때문에 일상생활에서 우리는 주로 비동기적 방식으로 생활한다. 반대로 비동기적 실행은, 어떤 일에 요청이 들어오면 그 요청에 대한 응답을 잠시 보류해두고 다른 추가적인 요청을 받아서 일정 시간이 지나면 들어온 요청에 대한 응답을 순차적으로 하나씩 돌려주는 응답처리 방식이다. 일상에서의 예로 설명을 하면 더 빠르게 이해할 수 있다.

example

<동기적 실행>
우리가 커피를 주문한다고 가정해 보자. a라는 사람이 커피를 주문하고 그 뒤로 b와 c가 커피 주문을 위해 대기 중이다. 카페 직원은 a의 커피를 주문 요청 즉시 제조를 시작하고 a가 주문한 커피를 받을 때까지 b와 c는 여전히 주문 대기 중이다. a가 커피를 받아서 카페를 나간 후에야 b는 커피를 주문할 수 있게 된다. b와 c역시 a의 응답에 대한 처리 방식과 동일한 방식으로 과정을 실행한다.

<비동기적 실행>
위와 동일하게 a, b, c 세 사람이 커피 주문을 위해 대기 중이다. a가 커피를 먼저 주문하고 나면 a의 커피를 즉시 만들지 않고 b의 주문을 먼저 받는다. 그 다음 c의 주문까지 받은 후 a의 커피를 제조하여 전달, b의 커피를 제조하여 전달, c의 커피를 제조하여 전달하면서 과정 실행이 종료된다.

우리는 일상생활에서 '동기', '비동기'라는 용어를 자주 사용하진 않지만 일상생활에서 일어나는 대부분의 응답처리를 우리는 자연스럽게 비동기적으로 실행하고 있다는 것을 알 수 있다.

비동기적 실행에서 가장 대표적으로 사용되는 메소드는 Promise 이다. Promise는 자바스크립트에 내장된 객체이며, 비동기 작업의 결과(완료 또는 실패)에 따라 해당 결과 값을 돌려주는 역할을 한다. 이름 그대로 '정해진 비동기 실행의 결과에 따라 완료 시의 데이터 또는 실패 시의 에러를 돌려주겠다고 약속하는' 것이다. Promise는 세 가지 상태(state)를 포함하는데 이 상태는

  • 대기(pending): 이행하거나 거부되지 않은 초기 상태.
  • 이행(fulfilled): 작업이 성공적으로 실행 완료됨.
  • 거부(rejected): 작업에 실패함.

다음으로 넘어가기 앞서, 우리가 프로미스를 사용하면서 주의해야할 점이 한 가지를 짚고 넘어가자.

프로미스는 새롭게 생성될 때, 인자로 넘겨준 resolve와 reject를 인자로 받는 executor 콜백함수가 자동적으로 곧바로 실행이 된다.

대기 중인 프로미스는 값과 함께 이행(resolve)할 수도 있고 어떤 오류로 인해 거부(reject)될 수도 있다. 코드로 예를 들면 다음과 같다.

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('success');
    }, 1000);
});

위와 같이 사용자가 필요로 하는 기능을 수행하는 비동기 promise를 producer라고 부르기도 한다.

또한 프로미스들은 then, catch, finally같은 메소드로 연결시켜 체이닝할 수 있다. 위의 producer 프로미스 객체가 받아온 정보를 받아서 사용하는 함수를 이 메소드들로 엮어서 데이터 처리를 할 수 있다. 코드로 예를 들면 다음과 같다.

promise.then(value => {
    console.log(value);
});

여기서 then은 프로미스가 성공적으로 이행되었을 때 프로미스에 resolve 객체로 전달된 값을 then 블록에서 받아와서 처리할 수 있도록 하는 기능을 한다. 반대로 프로미스를 성공적으로 이행하지 못했을 때 발생하는 에러 즉, reject 객체에 전달된 에러를 잡는 역할을 하는 메소드가 catch이다. reject 객체는 주로 Error()라는 객체와 함께 사용된다. 예제는 다음과 같다.

promise
    .then(value => {
        console.log(value);
    })
    .catch(error => console.log(error));

catch 함수가 실행되면 프로미스 실행 도중 발생한 에러를 핸들링할 수 있도록 에러를 말 그대로 잡아준다. catch 함수가 실행되면 프로미스의 reject 객체에 전달된 new Error()가 실행된다.

then 메소드에 이어 catch 메소드를 실행했을 때 에러를 잡을 수 있는 이유는 then 메소드가 위에서 썼던 producer 프로미스와 같은 프로미스를 그대로 리턴하기 때문에 catch메소드로 reject 객체를 호출할 수 있게 되는 것이다.

마지막으로 finally 메소드는 비교적 최근에 추가된 메소드로, 프로미스의 성공/실패 여부와 관계없이 마지막에 반드시 호출되는 함수이다.

promise
    .then(value => {
        console.log(value);
    })
    .catch(error => console.log(error))
    .finally(() => console.log('finally'));

Promise.all
프로미스의 다양한 메소드들 중 굉장히 유용한 Promise.all 메소드에 대해 간단히 정리해보자.

  • Promise.all([Promise1, Promise2 ...])

Promise.all은 순회 가능한(iterable) 객체(array)에 주어진 모든 프로미스를 이행한다. 만약 프로미스가 주어지지 않았다면 프로미스가 주어지지 않았을 때 이행하는 Promise를 반환한다. 기본적으로 반환값은 프로미스 형태이며 상황에 따라 여러가지 케이스로 나뉜다.
<반환값>

  • 매개변수로 주어진 배열이 비어 있으면 이미 이행한 Promise를 반환
  • 객체에 프로미스가 없으면 비동기적으로 이행하는 Promise를 반환
  • 그렇지 않은 경우, 대기 중인 Promise를 반환. 반환하는 프로미스의 실행 값은 매개변수로 주어진 프로미스의 순서와 일치하며, 완료 순서에 영향을 받지 않는다.

0개의 댓글