본문 바로가기
Front-end/javascript

[JavaScript] 콜백지옥과 promise

by 잔디🌿 2023. 7. 25.

    우선 콜백이 무엇인지 헷갈리시는 분은 아랫글을 참고해주세요

     

    https://ethereal-coder.tistory.com/32

     

    [JavaScript] 콜백함수

    콜백함수는 함수를 매개변수로 받는 함수이다. 예시 코드 function day(time, morning, night){ if(time === "morning"){ morning(); //wakeUp이 출력 } else{ night(); //goToSchool이 출력 } } function wakeUp(){ console.log("wakeUp"); } f

    ethereal-coder.tistory.com

     

    Promise는 정말 자바스크립트를 공부하면서 고비라고 생각할 정도로 어려웠다. 

    강의 영상을 정말 5번 정도 돌려봤는데도 이해가 안돼서 앞에 내용을 더 단단하게 이해하고 오니 바로 어느정도 습득했다!

    혹시 promise가 너무 어렵다고 생각하시는 분들은 콜백지옥의 코드를 정확히 이해하고 다시 보는 것을 추천한다.

     

     

    콜백지옥

     

    function taskA(a,b,cb){
      setTimeout(() => {
        const res = a+b;
        cb(res);
        },3000);
        }
        
     function taskB(a,cb){
      setTimeout(() => {
        const res = a *2 ;
        cb(res);
        },3000);
        }
        
     function taskC(a,cb){
      setTimeout(() => {
        const res = a * 5;
        cb(res);
        },3000);
        }
        
        //호출하기
        
        //2와 3을 더하고, 여기에 2를 곱하고, 5를 곱하도록 호출하기
        
        taskA(2,3,(a_res) = >{
           console.log(a_res); // 2+3 출력
            taskB(a_res,(b_res) => {
              console.log(b_res); //(2+3) *2 출력
                taskC(b_res,(c_res) => {
                  console.log(c_res); // (2+3)*2*5 출력
                        });
                     });
                  });
                  
                  
       console.log("코드끝");

    위 코드는 비동기적 함수가 있으므로, 코드 끝이 먼저 출력되고 나머지 것들이 차례로 출력된다. 

    위와 같이 비동기함수의 리턴 값을 또 다른 비동기 함수에 전달하고 또 전달하고를 반복하면 콜백지옥에 걸린다. 이를 구원하기 위한 것이 promise이다.

     

    Promise

     

    비동기 함수가 가질 수 있는 상태는 세가지이다.

    • Pending (대기상태) 
    • Fulfilled (성공)
    • Rejected (실패)

    대기상태에서 함수가 정상적으로 실행되면 성공, 아니면 실패이다.

    여기서 함수가 성공한 것을 resolve되었다 라고 하고 , 실패한 것을 reject 되었다고 한다.

     

    우선, 콜백함수만을 사용해서 받은 값이 양수인지, 음수인지 판단하는 코드를 만들었다.

    function isPostive(number, resolve, reject){
      setTimeout(()=> {
        if(typeof number === "number"){
    
          //성공(resolve했을 때라고 가정)
          resolve(number >= 0 ? "양수" : "음수");
      
        }
        else{
        //실패(reject 했을 때라고 가정)
          reject("값이 잘못 입력되었습니다.");
        }
      },2000);
    }
    
    isPostive(
      5,
      (res) => {
        console.log("성공 : ", res);
      },
      (err) => {
        console.log("실패 : ",err);
      }
    );

     이를 promise객체를 이용해서 다시 작성해보면

    function isPostive(number){
      const exeutor = (resolve, reject) => {
        setTimeout(()=> {
          if(typeof number === "number"){
      
            //성공(resolve했을 때라고 가정)
            resolve(number >= 0 ? "양수" : "음수");
        
          }
          else{
            reject("값이 잘못 입력되었습니다.");
          }
        },2000);
    
      };
    
      const tesk = new Promise(exeutor); //isPositive호출되면 바로 여기로
      return tesk;
    }
    
    const res = isPostive( 100 ); // number을 넣은 객체를 반환
    
    res.then((res) =>{
      console.log("성공 : ",res);
    }
    ).catch((err) =>{
      console.log("실패 :", err);
    })

    이렇게 된다. res에 isPositive를 호출하여 isPositive 내에 함수를 promise에 넣어 리턴한 값을 받고,

    res.then(성공했을 때 함수).catch(실패했을 때 함수) 로 호출하면 위와 같은 기능을 하는 코드가 만들어진다.

     

    그럼, 글 위쪽에서 콜백지옥을 설명했던 코드를 promise를 이용해서 짜보자.

     

    function taskA(a){
      return new Promise((resolve,reject) => {// promise 객체 리턴
        setTimeout(() => {
          const res = a+b;
          resolve(res);
          },3000);
      });
        }
        
     function taskB(a){
       return new Promise((resolve,reject) => {// promise 객체 리턴
        setTimeout(() => {
          const res = a *2 ;
          resolve(res);
          },3000);
          });
      }
    
    function taskC(a){
      return new Promise((resolve,reject) => {// promise 객체 리턴
        setTimeout(() => {
          const res = a * 5;
          resolve(res);
          },3000);
          });
      
        }
    
        taskA(2,3)                       //taskA의 promise 리턴됨
        .then((a_res) => {
          console.log("A result : ",a_res);
          return taskB(a_res);
        })                               //taskB의 promise 리턴됨
        .then((b_res) => {
          console.log("B result : ",b_res);
          return taskC(b_res);
        })                              //taskC의 promise 리턴됨
        .then((c_res) =>{
          console.log("C result", c_res);
        })

    위에 있는 task들은 매개변수로 하나의 정수로 만들고, promise객체를 리턴하도록 한다. 

    또한 호출하는 부분은 위와 같이 promise객체가 리턴될 때마다 then 을 이용해서 다음에 실행할 함수를 만들어준다. 이때 then이 여러개라 어색하다고 느낄 수 있는데, 주석 친 부분에서 promise 객체가 리턴되고 then은 이 각각의 객체에 대한 것이니까 문제가 없는 코드이다.

     

    이런 방식으로 코드를 짜면, 가독성이 높아진다는 장점도 있지만, 

     

    const task =  taskA(2,3) //상수를 만들어 여기다가 promise객체 받음
        .then((a_res) => {
          console.log("A result : ",a_res);
          return taskB(a_res);
        });
    
        console.log("안녕하세요"); // 다른코드
        
        task.then((b_res) => {  //위에서 받은 promise객체에 then으로 호출
          console.log("B result : ",b_res);
          return taskC(b_res);
        })
        .then((c_res) =>{
          console.log("C result", c_res);
        })

    위의 코드처럼 콜백 함수들을 실행하는 중간에 다른 코드를 넣을 수 있다는 장점이 있다.

    'Front-end > javascript' 카테고리의 다른 글

    [JavaScript] API 호출  (0) 2023.07.30
    [JavaScript] async와 await  (0) 2023.07.28
    [JavaScript] 동기, 비동기  (0) 2023.07.18
    [JavaScript] spread 연산자  (0) 2023.07.17
    [JavaScript] 비 구조화 할당  (1) 2023.07.17