본문 바로가기

Javascript/Javascript(2022 version)

Javascript 기초(8) - callback hell, Promise, async/await

728x90
반응형

1) Javascript 기초

   1-1) callback hell

   1-2) Promise

   1-3) async/await

 

 

 

 

 

1) Javascript 기초

학습 순서

  • callback
  • Promise
  • async/await
socket.on('data', (chunk) => {
  console.log(chunk);
})

// 비동기 코드: 언제 올지 모르는 코드
// 콜백 함수 안에서 다시 콜백 함수를 실행하는 것을 여러번 반복한 상황을 "callback hell"이라 부른다!!
// "callback hell"이 나올 수밖에 없는 이유는 비동기 코드를 처리하기 위함이다!
// 이 과정에서 "이벤트 기법"이라는 것을 활용해 처리함!
net.createServer((socket) => {
  socket.on('data', (chunk) => {
    const req = request(chunk);
  })
})

// 1. 서버를 연다.
// 2. 데이터를 받는다.

 

 

1-1) callback hell

// callback hell

const avante = () => {
  setTimeout(() => {
    console.log("avante go");
  }, 3000);
};

const sonata = () => {
  setTimeout(() => {
    console.log("sonata go");
  }, 2000);
};

const genesis = () => {
  setTimeout(() => {
    console.log("genesis go");
  }, 1000);
};

// 위의 상황의 경우, Client 입장에서 서버에게 요청을 한다면 3개의 요청을 보내고,
// 이에 대한 3개의 응답을 받는 상황이다!!


// 문제)
// 그럼 avante 요청 후 응답이 오면
// 그 후 sonata 요청 후 응답이 오게한 뒤
// 그리고나서 genesis를 요청하고 응답이 오도록 함!

const avante = () => {
  setTimeout(() => {
    console.log("avante go");
    sonata();
  }, 3000);
};

const sonata = () => {
  setTimeout(() => {
    console.log("sonata go");
    genesis();
  }, 2000);
};

const genesis = () => {
  setTimeout(() => {
    console.log("genesis go");
  }, 1000);
};

avante();

// 바로 위의 풀이의 문제점 : 확장성 문제(예를 들어 avante 함수를 실행시키면 반드시 sonata 함수를 실행시킬 수밖에 없는 구조이기 때문이다!!)


// 위의 문제를 해결하기 위한 코드형태
const avante = (callback) => {
  setTimeout(() => {
    console.log("avante go");
    callback();
  }, 3000);
};

const sonata = (callback) => {
  setTimeout(() => {
    console.log("sonata go");
    callback();
  }, 2000);
};

const genesis = (callback) => {
  setTimeout(() => {
    console.log("genesis go");
    callback();
  }, 1000);
};

avante(() => {
  sonata(() => {
    genesis(() => {
      console.log("end");
    });
  });
});

 

const avante = (callback) => {
  setTimeout(() => {
    console.log("avante go");
    callback();
  }, 3000);
};

const sonata = (callback) => {
  setTimeout(() => {
    console.log("sonata go");
    callback();
  }, 2000);
};

const genesis = (callback) => {
  setTimeout(() => {
    console.log("genesis go");
    callback();
  }, 1000);
};


// 문제2)
// avante 요청 후 응답받고,
// avante 요청 후 응답받고,
// genesis 요청 후 응답받고,
// sonata 요청 후 응답받고,
// avante 요청 후 응답받고,
// sonata 요청 후 응답받는 코드 구현
avante(() => {
  avante(() => {
    genesis(() => {
      sonata(() => {
        avante(() => {
          sonata(() => {});
        });
      });
    });
  });
});

 

 

 

1-2) Promise

Promise : 비동기 코드에 대한 "callback hell"을 해결하기 위해 등장함!

Promise는 기본적으로 return 값이 객체(Object)이다!!

// Promise 객체의 기본 형태
const pr = new Promise((resolve, reject) => {});

// Promise는 return 값이 객체(Object)이며(이는 new 키워드가 붙었다는 점에서 확인가능!), 생성자 함수의 인자값으로 무조건 함수를 받는다!!
// Promise의 2가지 매개변수
// 1) resolve : 성공했으면 resolve 함수의 인자값으로 넣어주라는 의미
// 2) reject : 실패했으면 reject 함수의 인자값으로 넣어주라는 의미

const pr = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("OK");
    // reject("Not OK");
  }, 3000);
});

console.log(pr);
// Promise { state : <pending>, result : undefined }
// Promise { state : <fulfilled>, result : 'OK' }
// Promise { state : <rejected>, result : 'Not OK' }

// Promise 객체는 기본적으로 "상태(state)"를 가진다!
// 여기서 상태(state)는 3가지 형태로 나뉘어져 있는데 이는 "pending(비동기 코드가 완료되지 않은 상태)", "fulfilled", "rejected"이다!
// "pending"은 아직 요청 중인 것이고 함수를 호출하게 되면 resolve이면 "fulfilled", reject이면 "rejected"이다!

// Promise 객체는 "then"과 "catch", 그리고 "finally" 메서드가 있다!
// then 메서드는 resolve이면서 상태가 "fulfilled"일 때 실행시켜 줌!!
pr.then((data) => {
  // then의 인자값(data)에는 Promise 객체의 result 값이 담긴다!
  console.log(data);
});

 

// 기존의 callback 함수를 Promise 객체로 변환해 봄!
const avante = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("avante go");
    }, 3000);
  });
};

const sonata = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("sonata go");
    }, 2000);
  });
};

const genesis = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("genesis go");
    }, 1000);
  });
};

// Promise 체이닝
console.time("x"); // 해당 코드를 통해 아래 코드 구문 실행 시 시간이 얼마나 걸리는지 체크할 수 있음!!
avante()
  .then((data) => {
    console.log(data);
    return sonata();
  })
  .then((data) => {
    console.log(data);
    return genesis();
  })
  .then((data) => {
    console.log(data);
    console.timeEnd("x");
  });
  
  
/*
// 문제3)
// avante 요청 후 응답받고,
// avante 요청 후 응답받고,
// genesis 요청 후 응답받고,
// sonata 요청 후 응답받고,
// avante 요청 후 응답받고,
// sonata 요청 후 응답받는 코드 구현

avante()
  .then((data) => {
    console.log(data);
    return avante();
  })
  .then((data) => {
    console.log(data);
    return genesis();
  })
  .then((data) => {
    console.log(data);
    return sonata();
  })
  .then((data) => {
    console.log(data);
    return avante();
  })
  .then((data) => {
    console.log(data);
    return sonata();
  })
  .then((data) => {
    console.log(data);
  });
*/

 

 

 

1-3) async/await

callback : tab index, 즉 들여쓰기가 너무 많다는 단점이 있다!

Promise : Promise 객체를 만드는 것이 어렵고, 사용할 때 조금 코드가 많다는 단점이 있다!

async/await : 위의 문제들을 해결하기 위해 등장함!

const avante = async () => {
  return "avante go";
};
console.log(avante()); // Promise 객체가 반환됨! <-- 여기서는 { <pending> }이 출력됨!

// avante().then((data) => {
//   console.log(data); // avante go
// });


// "async"를 달면 "await"을 쓸 수 있음!!
// await : 해당 함수 실행 후 then이 떨어질 때까지 기다렸다가 결과물(then 메서드의 인자값에 담기는 result 값)을 받아서 담아줌!!
// init 함수 : Promise 객체를 받아주는 함수(Promise 객체를 관리해주는 함수)
const init = async () => {
  const result = await avante(); // await이 되는 것은 반드시 Promise 객체여야 한다!!
  console.log(result);
};
init();


const avante = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("avante go");
    }, 3000);
  });
};

const sonata = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("sonata go");
    }, 2000);
  });
};

const genesis = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("genesis go");
    }, 1000);
  });
};


const init = async () => {
  const result = await avante(); // await이 되는 것은 반드시 promise 객체여야 한다!!
  console.log(result);
  const result2 = await sonata();
  console.log(result2);
  const result3 = await genesis();
  console.log(result3);
};
init();

/*
// 문제4)
// avante 요청 후 응답받고,
// avante 요청 후 응답받고,
// genesis 요청 후 응답받고,
// sonata 요청 후 응답받고,
// avante 요청 후 응답받고,
// sonata 요청 후 응답받는 코드 구현

const init = async () => {
  const result1 = await avante();
  console.log(result1);
  const result2 = await avante();
  console.log(result2);
  const result3 = await genesis();
  console.log(result3);
  const result4 = await sonata();
  console.log(result4);
  const result5 = await avante();
  console.log(result5);
  const result6 = await sonata();
  console.log(result6);
};
init();
*/


/*
// init2 함수를 실행시키면 해당 내용은 background에 들어감!!
let rst = "아무것도 없니?";
const init2 = async () => {
  const result = await avante();
  rst = result;
};
init2();
console.log(rst); // 아무것도 없니?
*/

(중요!!) "async"가 달려 있으면 해당 함수는 background에 들어가게 되고, 거기서 기다리다가 이후 실행된다!!

 

// Promise를 사용해야 하는 경우
// Promise.all([]);
const avante = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("avante go");
    }, 3000);
  });
};

const sonata = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("sonata go");
    }, 2000);
  });
};

const genesis = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("genesis go");
    }, 1000);
  });
};

const init = async () => {
  // const result1 = await avante();
  // console.log(result1);
  // const result2 = await genesis();
  // console.log(result2);
  // const result3 = await sonata();
  // console.log(result3);

  // 배열 안에 Promise 객체들만 들어갔을 경우 --> [Promise{}, Promise{}, Promise{}]
  /*
  Promise.all([avante(), sonata(), genesis()]).then((data) => {
    console.log(data);
  });
  */
  
  console.time("x");
  // const result = await Promise.all([avante(), sonata(), genesis()]);
  // console.log(result);
  
  // 배열 구조분해할당 활용
  const [a, b, c] = await Promise.all([avante(), sonata(), genesis()]);
  console.log(a, b, c);
  console.timeEnd("x");
};
init();