개발/Javascript

자바스크립트 Promise로 비동기 요청 처리하기

duknock 2022. 8. 31. 15:29
반응형

자바스크립트의 비동기요청을 처리하는 방법에 Promise 라는것이 있다.

 

Promise는 단어 뜻 그대로, "비동기 요청이 완료될 경우 어떠한 일을 이행하겠다." 를 약속한다.

 

한번 아래 코드를 보자.

 

let promise = new Promise(function(resolve, reject) {
	//비동기 요청 처리하는 곳
});

 

Promise는 new 메서드로 생성하는 객체다.

 

위 코드의 new Promise 에게 함수가 전달되는것을 볼 수 있는데, 이 함수를 executor(실행 함수)라고 한다.

 

단어가 어려워 보이지만 그냥 Promise 객체가 만들어질때 자동으로 실행되는 함수이다.

 

이 executor 안에 처리할 로직(보통 비동기 요청 넣겠지?)이 들어가면 된다.

 

그리고 executor에게 주어진 resolvereject 라는 콜백이 있는데

 

resolve 는 비동기 요청이 성공적으로 완료되었을 경우, reject 는 비동기 요청이 실패했을 경우 호출한다.

 

물론 코드도 작성 안했는데 지가 알아서 resolve나 reject를 호출하는건 아니고 요청 성공, 실패시 어떻게 처리할지는 작성해줘야 한다.

 

또한, new Promise로 생성된 Promise 객체는 state와 result 라는 프로퍼티를 가지는데

 

executor 함수가 실행되기 전에는 state : pending(대기), result : undefined 였다가

executor가 resolve를 콜백할경우(성공할 경우) state : fulfilled(이행함), result : value(결과값) 로 바뀌고

reject를 호출할경우(실패할 경우) state : rejected(실패함), result : error(에러) 로 바뀐다.

 

Promise 객체 (state : pending, result : undefined)
-> executor 실행
-> 
       ━━ 성공시 : resolve 콜백 -> Promise (state : fulfilled, result : value)
      ┗━  실패시 : reject 콜백 -> Promise (state : rejected, result : error)

 

 

그럼 성공, 실패 조건 없이 아래와 같이 코드를 짜면 어떻게 될까?

 

let promise = new Promise(function(resolve, reject){
    resolve("이행함");
    reject("실패함");
})

 

결과는 Promise {<fulfilled>: '이행함'} 이다.

 

Promise는 resolve 와 reject 중 하나만 호출한다. 둘 다 적는다고 resolve와 reject를 다 호출하지 않는다.

위 코드의 결과가 state : fulfilled인 이유는 resolve가 reject보다 위에 있어서 먼저 실행됐을 뿐이다.

 

resolve나 reject를 호출하기 전 Promise는 pending Promise(대기중인 프로미스)라고 하고, resolve 혹은 reject를 호출하고 나면, settled Promise(처리된 프로미스)라고 한다. settled Promise는 상태가 변하지 않으며, resolve 나 reject를 이후에 호출해도 무시한다.

 


 

오~ 그래서 요청에 따라서 Promise 상태가 변한다는건 알겠는데... 이걸 어떻게 사용함?

 

지금까지 Promise 객체가 executor 에 의해서 상태가 변하는 것을 공부했다면,

 

이제 그 변한 상태를 어떻게 활용하는지에 대해서 알아보겠다.

 


 

Promise 객체는 .then(), .catch(), .finally() 라는 메서드를 가지고 있다.

 

 

.then()

 

.then() 메서드는 Promise 처리에 가장 기본이 되는 메서드이다.

 

let promise = new Promise(function(resolve, reject) {
            setTimeout(() => {
                let rand = Math.ceil(Math.random() * 10);
                if(rand >= 5){
                    resolve("5보다 큼!");
                }
                else{
                    reject("5보다 작음!");
                }
            }, 500);
        });

        promise
        .then((result)=>{
            console.log(result);
        }, (error)=>{
            console.log(error);
        })

 

위 코드를 보면 executor 함수 내부에 0.5초 후에 실행되는 비동기 함수인 setTimeout()이 있고

 

rand 라는 변수에 0부터 10까지 무작위의 자연수가 들어간다.

 

만약 rand 가 5보다 크거나 같으면 resolve 를 호출하고

 

rand 가 5보다 작으면 reject를 호출한다.

 

 

그리고 아래에 보면 promise.then() 이런식으로 .then() 메서드를 사용한것을 볼 수 있는데,

 

.then() 메서드 안에 2개의 함수가 인수로 들어가있다.

 

첫번째 인수는 Promise가 성공적으로 이행되었을 때 실행되는 콜백이고,

두번째 인수는 Promise가 실패하였을 때 실행되는 콜백이다.

 

그리고 위 두 콜백함수의 안에 Promise의 result 값이 인수로 들어가게 된다.

 

그래서 첫번째 콜백으로 성공시 처리할 로직을 집어넣고, 두번째 콜백에 에러핸들링할 로직을 넣으면 된다....

 

근데 보통은 아래처럼 쓴다.

 

 

.catch()

 

.catch() 는 오직 Promise가 실패했을 때에만 사용되는 메서드이다.

 

let promise = new Promise(function(resolve, reject) {
            setTimeout(() => {
                let rand = Math.ceil(Math.random() * 10);
                if(rand >= 5){
                    resolve("5보다 큼!");
                }
                else{
                    reject("5보다 작음!");
                }
            }, 500);
        });

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

 

위에 .then() 으로 성공처리와 에러핸들링을 둘다 하는것보다 더 보기 편하고 직관적이다.

 

실전에서 쓸 때는 .then()으로 에러핸들링 하지말고 마지막에 .catch()를 꼭 넣어주자.

 

 

 

.finally()

 

.finally() 메서드는 Promise가 성공적으로 이행됐든, 실패했든 완료만 되면 실행되는 메서드이다.

 

주로 비동기요청 결과값이 도착하기전에 로딩 인디케이터(그 로딩중이라고 빙글빙글 돌아가는거)를 띄웠다가

요청이 완료되었을 때 안보이게 치워버리거나 할 경우 사용한다.

(요청 실패했다고 계속 로딩 인디케이터가 떠있으면 곤란하자너)

 

let promise = new Promise(function(resolve, reject) {
            setTimeout(() => {
                let rand = Math.ceil(Math.random() * 10);
                if(rand >= 5){
                    resolve("5보다 큼!");
                }
                else{
                    reject("5보다 작음!");
                }
            }, 500);
        });

        promise
        .finally(()=>{
            alert("Promise 처리 끝남!");
        })
        .then((result)=>{
            console.log(result);
        })
        .catch((error)=>{
            console.error(error);
        })

 

.finally() 메서드는 인수를 가지지 않는다. Promise의 성공, 실패 여부를 알 필요가 없기 때문이다.

 

또한 .finally() 메서드는 다음에 오는 메서드에게 result와 error를 전달한다.

 

그래서 보통은 .finally() 메서드를 가장 앞에 사용하고, 뒤에 .then() 메서드, 마지막에 .catch() 메서드를 사용한다.

 


 

이렇게 Promise 객체에 대하여 알아보았는데, 비동기 요청 자체가 자바스크립트의 동작순서와 별개로 움직이는 놈이다 보니 사용하기가 복잡하다.

 

다음에는 이 Promise를 더 쉽게 사용할 수 있는 async 와 await 문법에 대해서 알아보겠다

반응형