기록하는 개발자

[Javascript] 비동기처리 - 2. Promise 본문

Web/Javascript

[Javascript] 비동기처리 - 2. Promise

밍맹030 2022. 12. 2. 18:17
728x90

javascript의 비동기 처리 두번 째, Promise에 대한 글이다.

첫 번째 Callback에 대한 정리는 아래 링크에서 볼 수 있다.

 

https://mingmeng030.tistory.com/257

 

[Javascript] 비동기처리 - 1. Callback

javascript의 비동기 처리 방법에 대하여 callback , promise , async/await 세 파트로 나누어 정리를 해보려 한다. 그 중 첫 번째 Callback에 대한 글이다. Callback 아래 함수는 일반적으로 parameter를 받아 만든

mingmeng030.tistory.com

 

위 포스팅에서 Callback 의 문제점으로 '콜백  지옥'을 얘기하면서

이의 해결책으로 promise 또는 async/await를 사용한다고 언급했었다.

 

   Promise    

Promise프로미스는 비동기 작업을 조금 더 편하게 처리 할 수 있도록 ES6 에 도입된 기능으로,

데이터를 얻는데까지 지연시간이 발생하는 경우 해당 데이터에 접근하기 위한 방법을 제공한다.

 

아래는 이전 callback 을 사용하여 작성했던 getUserInfo 함수에 Promise를 적용한 코드이다.

 

   Promise 생성 방법   

Promise 객체는 new 키워드와 생성자를 통해 생성할 수 있으며, 생성자는 함수를 인자로 받는다.

인자로 받는 함수를 공식 문서에서는 executor 라고 칭한다.

 

1.    executor   는 첫 번째 인자로 resolve , 두 번째 인자로 reject를 받는다.

2.    resolve   는 executor 내에서 호출할 수 있는 함수로 promise 성공 시 호출한다. → 비동기 작업 성공

3.    reject   는 executor 내에서 호출할 수 있는 함수로 promise 실패 시 호출한다.  비동기 작업 실패 

 

 

생성 방법 1

const promise = new Promise(function(resolve, reject) { ... } );

생성 방법 2

function returnPromise() {
  return new Promise((resolve, reject) => { ... } );
}

-실제로는 변수에 할당하기보다는 함수의 리턴값으로 바로 사용되는 경우가 많고, 방법2의 화살표 함수 키워드를 더 많이 사용

 

 

예시

isMultipleOfTen 함수는 인자 num이 10의 배수가 아닌 경우 reject 함수가 호출되고

num이 10의 배수인 경우 resolve 함수가 호출된다.

function isMultipleOfTen(num) {
  return new Promise((resolve, reject) => {
    if (num%10!==0) reject(new Error(num+" is not multiple of 10"));
    else resolve(num+ " is multiple of 10");
  });
}

 

 

기존 함수는 정의한 뒤 호출을 해야만 함수가 실행되지만 

Promise는 new Promise(...) 를 통해 선언하는 순간 할당된 비동기 작업이 바로 시작된다.

 

Promise의 비동기 작업이 끝나고 난 다음의 동작은  then 메소드와 catch 메소드를 사용하여 설정한다.

1.   then   메소드는 해당 Promise 가 성공했을 때의 동작을 지정하며 함수를 인자로 받는다.

2.   catch   메소드는 해당 Promise 가 실패했을 때의 동작을 지정하며 함수를 인자로 받는다.

3.   then   과   catch   함수는 method chaining이라는 문법 패턴으로 사용될 수 있다.

 

 

method chaining의 예시

then catch 메서드는 또 다른 Promise 객체를 리턴한다.

그리고 이 Promise 객체는 인자로 넘긴 콜백 함수의 리턴값을 다시 then()과 catch() 메서드를 통해 접근할 수 있도록 해준다.

아래 예시처럼 then()과 catch() 메소드는 사슬처럼 연쇄적으로 호출할 수 있다.

let promise = new Promise(function(resolve, reject) {
    setTimeout(function() {
        resolve({ 
            "name" : "mingmeng",
            "email" : "mingmeng@email.com",
        });
    }, 1000);
});

promise
    .then(function(userObj) {
      console.log('[ userInfo ]');
      console.log('----------------------------------------------');
      console.log("username : " + userObj.name);
      console.log("email : " + userObj.email);
    })   
    .then(() => console.log("success"))
    .catch((error) => console.log("fail : ", error));

정상적인 인자를 넣은 경우 / 비정상적인 인자를 넣은 경우

 

isMultipleOfTen 함수의 다음 동작 처리 예시

function isMultipleOfTen(num) {
  return new Promise((resolve, reject) => {
    if (num%10!==0) reject(new Error(num+" is not multiple of 10"));
    else resolve(num+ " is multiple of 10");
  });
}

isMultipleOfTen(94810290)
  .then((result) => console.log("성공:", result))
  .catch((error) => console.log("실패:", error));

isMultipleOfTen(32424)
  .then((result) => console.log("성공:", result))
  .catch((error) => console.log("실패:", error));

코드 실행 결과

→ 실행 결과 정상적인 인자(10의 배수)를 넘긴 경우 then에 작성한 코드가 실행되고

비정상적인 인자(10의 배수가 아닌 수)를 넘긴 경우 catch에 작성한 코드가 실행되었음을 알 수 있다.

 

   Promise 단점   

Promise는  대기(pending)  이행(fulfilled)  ,  거부(rejected) 라는 세 가지 상태를 가진다.

 

 Pending(대기)  : 비동기 처리 로직이 아직 미완료인 상태

 Fulfilled(이행)  : 비동기 처리가 완료되어 promise가 결과 값을 반환해준 상태

 Rejected(실패)  : 비동기 처리가 실패하거나 오류가 발생한 상태

 

이행 상태일 때는 then, 거부 상태일 때는 catch 로 등록한 동작들이 실행된다.

앞서 언급한 비동기 작업의 성공과 실패는 각각 이행과 거부 상태였던 것이다.

그러나 우리가 Promise를 생성한 뒤 위 세 가지의 상태 중 실제로 어떤 상태인지 확인할 방법은 없다.

 

디버깅

method chaining으로 then이 연쇄적으로 사용되면서 오류가 발생하는경우

몇 번째 then에서 오류가 발생했는지 알 수 없어 디버깅이 어렵다.

 

예외 처리

Promise를 사용하면 try/catch 가 아닌 catch() 메서드를 사용하여 예외 처리를 해야한다.

이 때 동기 코드와 비동기 코드가 섞여 있을 경우 예외 처리가 난해해지거나 예외 처리를 누락하는 경우가 생기기 쉽다.

 

들여쓰기

실제 프로젝트에서는 위 예시들처럼 간단한 구조가 아닌 복잡한 구조의 비동기 처리 코드를 작성하게 된다.

따라서, then() 메서드의 인자로 넘기는 콜백 함수 내에서 조건문이나 반복문을 사용하거나

여러 개의 Promise를 병렬로 또는 중첩해서 호출해야하는 경우들이 발생하게 된다.

이 때, 다단계 들여쓰기를 해야할 확률이 높아지면서 코드 가독성은 점점 떨어지게 된다.

 

 

세 번째 포스팅의 주제인  async/await 을 사용하면, promise에 대한 문제점까지도 해결 할 수 있다.

 

[ 참고 ]

https://www.daleseo.com/js-async-promise/

https://www.daleseo.com/js-async-async-await/

https://springfall.cc/post/7

https://learnjs.vlpt.us/async/01-promise.html

 

728x90