JS (Java Script)

ES6 프로미스 패턴 !!("콜백지옥" 으로 부터 탈출)

GABOJOK 2023. 9. 13. 00:59

콜백함수

간단하게 말하면 어떤 함수의 인자 값으로 함수를 호출하는 거다. 

파라미터와 인자를 혼동할 수 있는데,

파라미터함수를 정의할 때에 선언할 때에 쓰이는 것.

전달인자는  함수를 호출 할 때에 전달되는 값.

function test (a){
	console.log('테스트' + a)   //테스트시작
}

test("시작")

위 예시에서는 a가 파라미터, "시작" 이라는 문자가 전달인자 라고 볼 수 있겟다. 

 

 

길고 복잡한 코드를 작성하다 보면, 콜백함수를 보기 어렵게 작성하게 될 수 있다. 예를 들어,

데이터를 불러온 다음에 setTimeout 함수가 실행되고, 그 다음에 어떤 데이터를 다시 불러온 다음에 이벤트리스너가 동작하도록 , 그리고 그 이벤트 리스너가 동작하면  또다른 setTimeout() 함수가 실행되는... 그런 복잡한 코드를 작성해야 한다고 해보자. 

 

그럴때 아주 유용한게 프로미스 패턴이다. 

 

 

프로미스 패턴 (Promise)

  • 비동기 처리를 위해 사용하는 것이 아니라, 단지 콜백함수 디자인의 대체품 이라고 생각할 것.
  • 생성자 함수(Constructor function)를 이용해서 프로미스 패턴을 생성한다. 
  • 파라미터는 2개가 있어야만 한다. 
  • exeutor 라는 콜백함수 2개 (resolve, reject) __ 성공하면 전달할것,, 실패하면 전달할것 이렇게 사용된다. 요청
  • 성공하면 실행처리를 해줄 .then(), 실패하면 실행처리를 해줄 .catch를 사용한다.        처리
  • 프로미스는 객체중 하나이다.

 

프로미스 상태

    1. pending : 판정대기중.

    2. resolved : 해결이 된, 성공이 된 상태

       fulfilled : 이행. 비동기 처리가 완료되어 프로미스가 결과값을 반환한 상태. resolve가 호출되면 "fulfilled"

    3. rejected : 실패한 상태  

 

 

프로미스를 만드는 순간 ___> exeutor 라는 콜백함수가 바로 실행이 된다. 

일반적인 함수처럼 호출을 해야 실행되는게 아니다!!!

 let promi = new Promise(function(resolve, reject){
	console.log('프로미스 패턴 실행')
 })

resolve() 👉 성공적으로 네트워크에서 혹은 파일에서 읽어온 데이터를 받아와서, 그것을 가공한 데이터를 resolve()라는 걸 통해 전달함.

reject() 👉  에러메세지 전달한다. 이때 catch를 쓰지 않으면 빨간 에러를 내뿜는다.

catch를 사용해야만, 오류 없이 콘솔창에 아주 조용히 에러 발생했다고 알려준다. 

 

let errorpromise = new Promise(function(resolve, reject){
    setTimeout(function(){
        reject(new Error ('인터넷연결불안정'));  
    })
})

 

이렇게 하고 새로고침 하면 에러가 뜬다. 에러 메세지만 보여줘야 하는데 말이다. 

Uncaught in promise 라고 말이다!! 에러가 컨트롤이 안되고 있다고 말하는거!!

이럴때 catch를 쓰면 에러를 내뿜지 않고, 에러메세지만 보여준다. 

 

정리해보면

resolve() 의 경우, .then() 이 실행되고,

reject() 의 경우, catch()가 실행되고, 

finally() 의 경우, 성공 실패와 관계없이 무조건 마지막에 호출되게 되는 인자이다.

          성공하면 resolve() 호출 된 뒤에 finally 실행됨. 무조건!! reject도 동일

 

 

이번엔 프로미스의 체이닝  예시이다. 

 

resolve('hey') 이렇게 프로미스 함수에 적어두었다면,

.then (function(value){  })

여기서 value 에 hey가 전달되는 식이고, 그것이 연결연결 되어 복잡한 코드를 좀더 편하게 짤 수 있다.

인자값의 이동과 변화를 유심히 봐보자.

let testPromise = new Promise(function (resolve, reject){
	setTimeout(function(){
    	resolve(3)
    }, 1000)  // 1초 뒤에 resove() 실행시켜주는데, 그 안에 인자로 3 넣어주.
})

testPromise
	.then(function(num){   //아까 resolve에 넣은 인자 3 여기 num에 쏙 들어감. 
    	num*2              //6
    }).then(function(num){  //성공했으니 여기 num에는 계산 완료된 6이 연관되어 들어감. 
    	num*3               //18
    }).then(function(num){  //성공했으니 여기 num에는 계산 완료된 18이 연관되어 들어감. 
    	return new Promise(function(resolve, reject){   //성공의 결과로 또다른 프로미스 생성.
        	setTimeout(function(){    
            	resolve(num-1)    //성공하면 1초뒤에 num-1 값을 resolve에 넣어서 다음 호출에 사용해줘
            }, 1000)
        })
    }).then(function(num){   //성공했으니 여기 num에는 17을 쏙 넣어줘. 
    	console.log(num)
    })

* then은 값을 바로 전달해도 되고, 프로미스를 전달해도 된다. 

 

 

 

아래 예시를 setTimeout 함수를 서버라고 가정하고  봐보장

 함수의 인자값이 계속 연결되어서

종이 => 펜 => 책

이라는 출력결과가 3초 뒤에 나오게 된다. 

let getPaper = () => 
	new Promise((resolve, reject) => {
    	setTimeout(() => resolve('종이'), 1000);  
    });
    
let getPen = paper => 
	new Promise((resolve, reject) => {
    	//setTimeout(() => resolve(`${paper} => 펜`), 1000);
        setTimeout(() => reject(new Error(`error  ${paper} => 펜`)), 1000)
    });
let getBook = pen => 
	new Promise((resolve, reject) => {
    	setTimeout(() => resolve(`${pen} => 책`), 1000);
    });
    
getPapter()
    .then(paper => getPen(paper))
    .catch(error => {return '색연필';})  //getPen 에서 reject 된 경우 색연필 => 책
    .then(pen => getBook(pen)) 
    .then(look => console.log(look))   //모두 resolve로 처리가 된 경우. 종이 => 펜 => 책
    //만약 catch문에 단순 console.log를 찍는다면, 
    //.catch(console.log)   //Error: error  종이 => 펜

 

실패했을 경우 

reject(new Error())

.catch문을 단순히 .catch(console.log)를 통해 어디서 에러가 발생했는지 확인 할 수 있다. 

그렇지만 혹 에러가 발생했을 경우 대체할수 있는 데이터를 넣어주고 싶다면 방법이 있다. 

 

 

실패했을 경우  대체할 데이터 넣기. 

여러개의 프로미스 패턴이 연결되어 있는 상황에서 요청이 실패했을 경우에, 

 

 

꿀팁

콜백함수에서 받아오는 값을 바로 다음 함수에 인자로 넣어주는 경우 생략 가능하다. 

👉     .then(paper => getPen(paper))

👉     .then(getPen)