본문 바로가기
경일/Javascript

[Javascript] 콜백지옥과 프로미스

by dev_kong 2022. 1. 28.
728x90
728x90

0. 목차

1. 콜백지옥

2. 프로미스

1. 콜백지옥

프로미스를 이해하기 위해선

콜백 지옥을 이해해야 한다.

콜백지옥을 이해하기 위해선 

콜백펑션을 이해해야 한다.

.. ㅎ

https://kong-dev.tistory.com/116?category=991165 

 

[Javascript] 콜백함수

0. 목차 1. 개요 2. 콜백함수란 1. 개요 setTimeout(() => { console.log('hello world'); }, 1000); 이 코드를 보자. 콜백함수에 대한 개념이 잡히기 전의 나는 이 코드를 1000ms 가 지난뒤에, 에로우 펑션 안의..

kong-dev.tistory.com

최근에 정리한 콜백함수에 대한 글.

기억이 안나면 다시 보면 될듯.

 

우선 억지로 억지로 콜백 지옥을 하나 만들어 보자.

function randomTime() {
  return Math.floor(Math.random() * 10) * 1000;
}

function getChicken() {
  setTimeout(() => {
    console.log('동묘시장 -> chicken');
  }, randomTime());
}

function getEgg() {
  setTimeout(() => {
    console.log(`동묘시장 -> chicken -> egg`);
  }, randomTime());
}

function getMeal() {
  setTimeout(() => {
    console.log(`동묘시장 -> chicken -> egg -> fried egg`);
  }, randomTime());
}

getChicken();
getEgg();
getMeal();

 

이런 함수가 있을 때, 

출력 순서를 맞추는게 가능할까?

찍어서 맞추는 걸 제외한다면

순서를 정확히 알아내는 건 불가능하다.

세개의 함수가 모두 비동기로 처리되고,

처리되는데 걸리는 시간이 모두 랜덤하게 정해지기 때문에,

출력되는 순서를 알아내기가 불가능하다.

 

그런데 만약 비동기로 처리되는 이 코드들의 순서를 정해주고 싶다면?

함수안에 함수를 넣는 방법, 즉 콜백펑션을 이용해서 이를 해결 할 수 있다.

 

function randomTime() {
  return Math.floor(Math.random() * 10) * 1000;
}

function getChicken(callback) {
  setTimeout(() => {
    console.log('동묘시장 -> chicken');
    callback();
  }, randomTime());
}

function getEgg(callback) {
  setTimeout(() => {
    console.log(`동묘시장 -> chicken -> egg`);
    callback();
  }, randomTime());
}

function getMeal(callback) {
  setTimeout(() => {
    console.log(`동묘시장 -> chicken -> egg -> fried egg`);
    callback();
  }, randomTime());
}

getChicken(() => {
  getEgg(() => {
    getMeal(() => {});
  });
});

 

이런식으로.. 근데 에러 처리 안해줄거임?

에러 처리 해줘야 되자늠

 

function randomTime() {
  return Math.floor(Math.random() * 10) * 1000;
}

function errorFunction() {
  console.log('재고가 없습니다.');
}

function getChicken(callback, errorFunction) {
  if (callback) {
    setTimeout(() => {
      console.log('동묘시장 -> chicken');
      callback();
    }, randomTime());
  } else {
    errorFunction();
  }
}

function getEgg(callback, errorFunction) {
  if (callback) {
    setTimeout(() => {
      console.log(`동묘시장 -> chicken -> egg`);
      callback();
    }, randomTime());
  } else {
    errorFunction();
  }
}

function getMeal(callback, errorFunction) {
  if (callback) {
    setTimeout(() => {
      console.log(`동묘시장 -> chicken -> egg -> fried egg`);
      callback();
    }, randomTime());
  } else {
    errorFunction();
  }
}

getChicken(() => {
  getEgg(() => {
    getEgg(() => {}, errorFunction);
  }, errorFunction);
}, errorFunction);

 

출력 순서는

 

동묘시장 -> chiken

동묘시장 -> chiken -> egg

동묘시장 -> chiken -> egg -> fried egg

 

 

이렇게 맞춰는 졌다.

근데 모양이 기괴하다.

꼴랑 함수 세개인데도 해석하기 까다로운 코드가 돼버렸다.

 

이걸 깔끔하게 만들어 주기 위해서는 프로미스를 사용해야 한다.

2. 프로미스

 

function randomTime() {
  return Math.floor(Math.random() * 10) * 1000;
}

function errorFunction() {
  console.log('재고가 없습니다.');
}

function getChicken() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('동묘시장 -> chicken');
      resolve(getEgg);
    }, randomTime());
  });
}

function getEgg() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('동묘시장 -> chicken -> egg');
      resolve(getMeal);
    }, randomTime());
  });
}

function getMeal() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('동묘시장 -> chicken -> egg -> fried egg');
    }, randomTime());
  });
}

getChicken()
  .then((data) => data())
  .then((data) => data())
  .then((data) => console.log(data));

 

프로미스를 사용해서 이렇게 변경할 수 있다.

 

모든 함수를 프로미스 생성자 함수로 만들어진 인스턴스 객체를 리턴하게끔 리턴 값을 정해주고,

resolve 함수의 인자값이 then method의 콜백함수의 인자값으로 들어간다.

then method는 프로미스 인스턴스 객체 내부에서 resolve가 실행 되지 않으면, 실행되지 않는다.

즉, then method의 체이닝을 통해 비동기적으로 처리되는 코드의 실행 순서를 정할 수 있다.

 

then method의 콜백함수의 인자값을 resolve 함수의 인자값에서 전달받는게 조금 이해가 어려워서

위 코드를 조금 변경해 보았다.

 

function randomTime() {
  return Math.floor(Math.random() * 10) * 1000;
}

function errorFunction() {
  console.log('재고가 없습니다.');
}

function getChicken(market) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${market} -> chicken`);
    }, randomTime());
  });
}

function getEgg(process) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${process} -> egg`);
    }, randomTime());
  });
}

function getMeal(process) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${process}-> fried egg`);
    }, randomTime());
  });
}

getChicken('동묘시장')
  .then((data) => getEgg(data))
  .then((data) => getMeal(data))
  .then((data) => console.log(data));

하나하나 뜯어보면

getChicken 인수에 '동묘시장'  이 들어갔고

getChicken 은 프로미스 인스턴스 객체를 리턴한다.

프로미스 인스턴스 객체 내부에서 resolve 함수가 실행되면,

전달받은 인자값인 '동묘시장' 이 참조되어,

'동묘시장 -> chicken' 이란 값이

첫번째 then의 콜백함수의 인자값으로 전달된다.

 

전달된 인자값은 getEgg의 인자값으로 전달되고

getEgg 함수는 프로미스 인스턴스 객체를 리턴한다.

프로미스 인스턴스 객체 내부에서 실행된 resolve 함수에 '동묘시장 -> chicken' 이 전달되고,

resolve 함수는 '동묘시장 -> chicken -> egg' 를 

두번째 then 의 콜백함수의 인자값으로 전달한다.

 

전달한 인자값은 getMeal함수의 인수로 들어가고,

getMeal 함수는 프로미스 인스턴스 객체를 리턴한다.

그리고 그안에서 실행된 resolve함수에 '동묘시장 -> chicken -> egg' 가 전달되고,

resolve 함수는 '동묘시장 -> chicken -> egg -> fried egg' 를 

마지막 then method의 콜백함수의 인자값으로 전달 된다.

그리고 전달된 인자값을 출력한다.

 

결국 위 함수는  동묘시장 -> chicken -> egg -> fried egg 를 출력하게 된다.

..ㅎ 기나긴 과정이다.

 

여기서 arrow function의 특징 중 하나인 전달하는 인자값을 다른 연산 없이 return값에 지정된 함수의 인자로 사용되면

그냥 실행할 함수의 이름만 적어줘도 된다.

 

어.. 그러니까 코드를 보면

 

function randomTime() {
  return Math.floor(Math.random() * 10) * 1000;
}

function errorFunction() {
  console.log('재고가 없습니다.');
}

function getChicken(market) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${market} -> chicken`);
    }, randomTime());
  });
}

function getEgg(process) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${process} -> egg`);
    }, randomTime());
  });
}

function getMeal(process) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${process}-> fried egg`);
    }, randomTime());
  });
}

getChicken('동묘시장') //
  .then(getEgg)
  .then(getMeal)
  .then(console.log);

 

이렇게 적어도 잘 실행 된다는 거임. ㅇㅇㅇ

계란 후라이 먹기 참 힘들다.. ^^

728x90
728x90

'경일 > Javascript' 카테고리의 다른 글

[Javascript] async 와 await  (0) 2022.01.30
[Javascript] 콜백함수  (0) 2022.01.10
[Javascript] 객체의 비교와 복사  (0) 2022.01.07
[Javascript] 메서드와 프로토타입  (0) 2022.01.06
[문제풀이] 문자열 갖고 놀기..?  (0) 2022.01.06

댓글