공부/React.js

[React.js] React 사용을 위한 자바스크립트 응용 - Promise

데부한 2022. 4. 30. 21:51
반응형

 

React 게시글은 대부분 인프런의 '한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지' 강의를 기반으로 내용을 정리했습니다.

 

Promise

동기와 비동기에 대해 포스팅했을 때 마지막에 콜백 지옥(callback hell)을 잠깐 얘기했었다. Promise에 대해 알아보기 전에 먼저 비동기 작업이 가질 수 있는 3가지 상태에 대해 알아보자.

 

비동기 작업이 가질 수 있는 3가지 상태

Pending

  • 현재 비동기 작업이 진행중이거나, 현재 이 작업이 시작할 수 없는 문제가 발생했음을 의미한다.

Fulfilled

  • 이행 또는 성공 상태로 이 비동기 작업이 우리가 의도한대로 정상적으로 완료된 상태를 의미한다.

Rejected

  • 거부 또는 실패 상태로 비동기 작업이 모종의 이유(서버 비응답, 시간 지연 등)로 인해 실패했음을 의미한다.

resolve

  • 대기 상태에서 성공 상태로 변화하는 과정을 resolve(해결)이라 한다.

reject

  • 대기 상태에서 실패 상태로 변화하는 과정을 reject(거부)라고 한다.
반응형

resolve와 reject를 코드로 구현해보자.
2초 뒤에 비동기적으로 입력받은 값이 숫자인지 아닌지 판별하고, 만약 숫자면 양수와 음수인지 판별해주는 프로그램이다.

function isPositive(number, resolve, reject) {
  setTimeout(() => {
    if(typeof number === 'number') {
      // 성공 -> resolve
      resolve(number >=0? "양수" : "음수");
    } else {
      // 실패 -> reject
      reject("주어진 값이 숫자형이 아닙니다.");
    }
  }, 2000)
}

isPositive("12", (res)=>{
  console.log("성공적으로 수행됨 : " + res);
}, (err)=>{
  console.log("실패하였음 : ", err) // 출력
});

위의 코드는 비동기, 콜백 함수를 이용해서 만든 기본적인 프로그램이다. 이번에는 Promise를 사용해서 코드를 작성해보자.

 

function isPositiveP(number) {
  const executor = (resolve, reject) => {
    setTimeout(()=>{
      if(typeof number === 'number') {
        // 성공 -> resolve
        console.log(number);  // 101 출력
        resolve(number >=0? "양수" : "음수");
      } else {
        // 실패 -> reject
        reject("주어진 값이 숫자형이 아닙니다.");
      }
    }, 2000);
  };
};

isPositiveP(101);

Promise로 바꾸기 위해 새로운 함수를 만들었다. 아까 만든 코드와 다른 점은 executor 함수를 생성했다는 것이다. executor는 실행자. 즉 비동기 함수를 실질적으로 실행시키는 함수라고 일단 생각하면 된다. 

반응형
function isPositiveP(number) {
  const executor = (resolve, reject) => {
    setTimeout(()=>{
      if(typeof number === 'number') {
        // 성공 -> resolve
        console.log(number);
        resolve(number >=0? "양수" : "음수");
      } else {
        // 실패 -> reject
        reject("주어진 값이 숫자형이 아닙니다.");
      }
    }, 2000);
  };

  const asyncTask = new Promise(executor);
  return asyncTask;
};


const res = isPositiveP(101);

Promise 사용을 위해 asyncTask Promise 타입 상수를 만들고, 그 상수를 return한다. isPositiveP()를 호출하면 이 return 값을 받기 위해 res라는 상수를 선언 후 호출하는 식으로 변경했다.
Promise 타입을 리턴하는 명령을 작성하면 함수의 설명에 Promise 객체를 반환한다고 업데이트된다.

 

이렇게 반환받은 Promise 객체를 사용하는 방법은 than과 catch를 사용하면 된다.

res.then((res)=>{
  console.log("작업 성공 : " , res);
})
.catch((err) => {
  console.log("작업 실패: ", err)
});

resolve를 수행한 결과 값을 then으로 가져올 수 있고, reject를 수행한 결과 값을 catch로 가져올 수 있다.

반응형

Promise를 이용해 콜백 지옥을 탈출하는 예제를 만들어보자. 저번 포스팅에 있었던 taskA, taskB, taskC 함수들!

function taskA(a, b, cb) {
  setTimeout(()=> {
    const res = a+b;
    cb(res);
  }, 3000)
};

function taskB(a, cb) {
  setTimeout(()=> {
    const res = a * 2;
    cb(res);
  }, 1000)
};

function taskC(a, cb) {
  setTimeout(()=> {
    const res = a * -1;
    cb(res);
  }, 2000)
};

taskA(4, 5, (a_res) => { 
  console.log("A result : ", a_res); 
  taskB(a_res, (b_res) => { 
    console.log("B result : ", b_res); 
    taskC(b_res, (c_res) => { 
      console.log("C result : ", c_res); 
    }) 
  }) 
})

위 코드를 Promise로 변경해보자.

 

function taskA(a, b) {
  return new Promise((resolve, reject) => {
    setTimeout(()=> {
      const res = a+b;
      resolve(res);
    }, 3000)
  });
};

function taskB(a) {
  return new Promise((resolve, reject) => {
    setTimeout(()=> {
      const res = a * 2;
      resolve(res);
    }, 1000)
  })
}

function taskC(a) {
  return new Promise((resolve, reject) => {
    setTimeout(()=> {
      const res = a * -1;
      resolve(res);
    }, 2000)
  })
}

const res = taskA(5, 1);

res.then((a_res)=> {
  console.log("A result : ", a_res);
  taskB(a_res).then((b_res) => {
    console.log("B result : " , b_res);
    taskC(b_res).then((c_res) => {
      console.log("C result : ", c_res);
    });
  }); 
});

then을 이용해 콜백을 호출해봤는데....? 콜백 함수와 같은 then 지옥이 펼쳐졌다. 왜 이렇게 된거냐면 then을 일반 콜백함수 부르듯이 사용해서 그렇다. 호출 부분만 다시 변경해보자.

반응형
taskA(5, 1)
.then((a_res) => {
  console.log("a result : ", a_res);
  return taskB(a_res);
})
.then((b_res) => {
  console.log("b result : ", b_res);
  return taskC(b_res);
})
.then((c_res) => {
  console.log("c reult : ", c_res);
});

이번엔 return문이 새로 생겼다. 쉽게 말하자면 그냥 a_res의 값을 taskB함수에 매개변수로 주면서 호출함과 동시에 taskA의 콜백은 종료된다. 이렇게 then을 연쇄적으로 사용하는 것을 then chaining이라고 한다. 나는 음 솔직히 일반적인 콜백 함수나 then chaining이랑 뭐가 다른지 모르겠다. 생김새만 다른 거 같은데....라고 하는 순간 새로운 설명이 추가 됐다.

Promise를 이용하면 비동기 처리를 호출하는 코드와 결과를 처리하는 코드를 분리해줄수 있는 장점이 있다.

const bPromiseResult = taskA(5, 1)
.then((a_res) => {
  console.log("a result : ", a_res);
  return taskB(a_res);
});

bPromiseResult.then((b_res) => {
  console.log("b result : ", b_res);
  return taskC(b_res);
})
.then((c_res) => {
  console.log("c reult : ", c_res);
});

이런식으로.... 근데 아직 많이 안써봐서 그런지 뭐가 더 좋은건지 와닿지는 않는다.

 

반응형