개발기록장

[JavaScript] 콜백함수와 콜백 지옥 탈출을 위한 promise, async/await (2) 본문

TIL/JavaScript

[JavaScript] 콜백함수와 콜백 지옥 탈출을 위한 promise, async/await (2)

yangahh 2020. 12. 30. 05:30

 

이전 글

2020/12/30 - [TIL/JavaScript] - [JavaScript] 콜백함수와 콜백 지옥 탈출을 위한 promise, async (1)

 

[JavaScript] 콜백함수와 콜백 지옥 탈출을 위한 promise, async (1)

콜백함수란? 자바스크립트에서는 파라미터로 함수를 전달 받을 수 있는데 이때 파라미터로 전달받아서 함수 내부에서 수행되는 이 함수를 콜백함수라고 한다. 예시) // 예제 1 : 동기적(=즉각적)

devvvyang.tistory.com

 

Promise 란?

 

자바스크립트에서 제공하는 비동기를 간편하게 처리할 수 있도록 도와주는 오브젝트로,

콜백 함수를 대신하여 유용하게 사용된다.

 

network통신, file 읽어오기 등과 같은 시간이 오래 걸리는 작업을 진행할 때 promise를 만들어서 비동기로 처리하는 것이 좋다.

 

promise는 정해진 장시간의 기능을 수행하고 나서 정상적으로 기능이 수행되었다면 처리된 결과 값을 전달해주고,

수행 중 문제가 발생했다면 에러를 전달해준다.

 

 

 

Promise 사용 2가지 포인트

 

1. state

: 프로세스가 operation을 수행하고 있는 중인지(=pending), 완료가되어 성공했는지(=fulfilled) 실패했는지(=reject)

 

 

2. 데이터를 제공하는 쪽(producer)과 소비하는 쪽(consumer)

 

  - Producer : promise를 만들어 데이터를 제공하는 쪽

  • Promise는 클래스이기 때문에 오브젝트를 만들어줘야 한다.
  • Promise 오브젝트가 생성되면, executer(파라미터로 넘겨준 콜백함수)는 자동적으로 수행된다.
const promise = new Promise((resolve, reject) => {

  // resolve는 기능을 정상적으로 수행해서 마지막에 최종 데이터를 전달하는 콜백함수. 
  // reject는 기능을 수행하다가 중간에 문제가 생기면 호출하게 될 콜백함수.

  console.log("doing something..."); // 이 코드는 promise가 만들어진 순간 바로 실행됨
  
  setTimeout(() => {
    // 네트워크 통신을 통해 데이터를 가져온다고 가정(비동기식 수행을 위해)
    resolve("success"); // 기능을 성공적으로 수행했을 경우 받아온 데이터(success)를 전달해주는 기능
    // reject(new Error('no network')); // Error라는 클래스는 자바스크립트에서 제공하는 오브젝트 중 하나.
  }, 2000);
});

 

  - Consumer : 만들어진 promise를 사용하는 쪽

  • then, catch, finally를 이용해 값을 받아올 수 있다.
  • then은 기능이 정상적으로 수행되었을 때. 즉, resolve가 수행된 후 결과 값을 받아와 수행
  • catch는 에러가 발생했을 때. 즉, reject가 수행되고 나서 error값을 받아와 수행
  • finally는 성공, 실패와 상관없이 무조건 수행
promise
  .then((value) => {
    console.log(value);  // 결과 : success
  })
  .catch((error) => {
    console.log(error);  // 결과 : no network
  })
  .finally(() => {
    console.log("finally");
  });

 

 

 


 

 

Promise Chaining 

 

예제 1) 서버에서 숫자를 받아오는 promise

const fetchNumber = new Promise((resolve, reject) => {
  setTimeout(() => {
    // 서버 통신을 한다고 가정
    resolve(1); // 2초 있다가 숫자 1을 전달
  }, 2000);
});

fetchNumber
  .then((num) => num * 2) // 성공적으로 숫자를 받아오면 그 수에 2를 곱하고
  .then((num) => num * 3) // 2를 곱한수를 또 3으로 곱하고
  .then((num) => {
    // 그 숫자를 다른 서버에 전달해서 다른 숫자로 변환된 값(1뺀 값)을 받아옴. then은 값을 바로 전달할 수도 있고, promise를 전달할 수도 있다.
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve(num - 1), 1000);
    });
  })
  .then((num) => console.log(num));

 

예제 2)

const getHen = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve("🐓"), 1000);
  });

const getEgg = (hen) =>
  new Promise((resolve, reject) => {
    // setTimeout(() => resolve(`${hen} -> 🥚`), 1000);
    setTimeout(() => reject(new Error(`error! ${hen} => 🥚`)), 1000);
  });

const cook = (egg) =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${egg} => 🍳`), 1000);
  });

getHen()
  .then((hen) => getEgg(hen)) // .then(getEgg) 이렇게 생략 가능
  .catch((error) => {
    return "🍞";
  }) // 전체적인 promise chain에 문제가 발생하지 않도록 promise에서 실패했을때 에러로 처리하지 않고 대체값을 주는 방법
  .then((egg) => cook(egg)) // .then(cook) 이렇게 생략 가능
  .then((result) => console.log(result)); // .then(consol.log) 이렇게 생략 가능
//   .catch((error) => console.log(error));

 

 


 

 

콜백 지옥을 promise로 바꿔보기

 ** 이전 글 참고

// Callback Hell 예제를 promise로 해결하는 방법


class UserStorage {
  loginUser(id, password) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (
          (id === "user1" && password === "pwpw1") ||
          (id === "user2" && password === "pwpw2")
        ) {
          resolve(id);
        } else {
          reject(new Error("not found"));
        }
      }, 2000);
    });
  }

  getRoles(user) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (user === "user1") {
          resolve({ name: "user1", role: "admin" });
        } else {
          reject(new Error("no access"));
        }
      }, 1000);
    });
  }
}


// 구현할 프로세스
// 1. 사용자에게 id, password 입력받기
// 2. 로그인
// 3. 로그인 성공 시 받아온 id로 role 받아오기
// 4. 성공적으로 role을 받아오면 name과 role 출력


let id = prompt("id를 입력해주세요.");
let password = prompt("password를 입력해주세요.");

let userStorage = new UserStorage();

userStorage
  .loginUser(id, password)
  .then(userStorage.getRoles) //.then((user) => userStorage.getRoles(user))와 같은 표현
  .then((userWithRole) =>
    alert(`Hello ${userWithRole.name}! you have a ${userWithRole.role} role.`)
  )
  .catch(alert); // .catch((error) => alert(error))와 같은 표현