[ TIL ] - 비동기 처리 (callback/ promise/ async/ await)

Gorae·2021년 7월 26일
0

(TIL) Javascript

목록 보기
2/3
post-thumbnail
드림코딩 by 엘리님 강의를 참고한 내용입니다.

https://youtu.be/JB_yU6Oe2eE

동기 / 비동기

  • js는 hoisting 이후 순서에 따라 동기적으로 실행되는 언어이다.
    (hoisting: var 변수, 함수 선언 등이 자동으로 가장 위로 올라가는 것)
  • 비동기 : 언제 실행될 지 정확하게 알 수 없는 것.

콜백 함수

  • 콜백 함수란, 필요할 때 불러서 쓰는 것으로, 보다 더 발전한 것이 프로미스다.
  • 콜백 함수는 동기또는 비동기로 만들 수 있다.
// 동기적으로 실행되는 콜백 함수 예시(인자로 함수를 받아서, 바로 실행함)
function printWord(print) {
  print();
}

printWord(() => console.log('word')); 

프로미스(Promise)

  • 콜백 함수보다 더 직관적이며, 발전한 형태.
  • javascript에서 비동기를 간편하게 처리할 수 있도록 제공하는 object.
  • 정상 기능 수행 시 성공값 출력, 에러 발생 시 에러 메세지 출력해줌.
  • 진행 상태(PromiseState) 파악이 중요( pending -> fulfilled or rejected )
  • Producer, Consumer 로 나뉨
// Producer
const promise = new Promise((resolve, reject) => {
  // 보통 promise는 비동기로 실행되어야 하는 무거운 작업이 들어옴 
  // promise 는 생성됨과 동시에 콜백 함수가 실행됨
  setTimeout(() => {
    // resolve("성공!");
    // reject(new Error('no network'));
  }, 2000);
}
                            

// Consumer : then, catch, finally 사용
promise
  .then(value => {
  // resolve 가 잘 수행된다면,
  // resolve 로 전달된 값이 value로 들어옴
    console.log(value);
  })
  .catch(error => {
  // resolve 에 실패하여 reject가 수행된다면,
  // 전달된 error 값을 출력 가능
    console.log(error);
  })
  .finally(() => {
  // finally 는 성공하든 실패하든 무조건 실행됨.
    console.log("finally");
  });                       
  • then새로운 promise 를 전달할 수도 있다.
const fetchNum = new Promise((resolve, reject) => {
  setTimeout(() => resolve(1), 1000);
});

fetchNum
  .then(num => num * 2)
  .then(num => {
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve(num - 1), 1000);
    });
  })
  // 한 가지만 받아서 그대로 전달하는 경우, .then(console.log)으로도 작성 가능하다.
  .then(num => console.log(num));

// 1 출력

async/await

  • syntactic sugar
    (새로운 것을 추가하는 게 아니라, 기존에 있는 프로미스 위에 덧붙여진 것)
  • function 앞에 async 를 붙이면 자동으로 Promise로 만들어진다.
  • 프로미스를 동기식으로 작성한 것처럼 보이게
  • 프로미스를 여러 번 중첩해야 하는 것의 대안이 됨!
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// async 는 함수 앞에, await 은 async 함수 안에서 사용.
// 🍎 를 출력하는 함수
async function getApple() {
  await delay(3000);
  return "🍎";
}
// 🍑 를 출력하는 함수
async function getPeach() {
  await delay(3000);
  return "🍑";
}

// 🍎 + 🍑 를 출력하는 함수 : 0 ~3

	// 0. 프로미스로 작성(여러 번 중첩되는 것을 볼 수 있음)
function pickFruits() {
  return getApple().then(apple => {
    return getPeach().then(peach => `${apple} + ${peach}`);
  });
}
pickFruits().then(console.log);
// 🍎 + 🍑 출력


	// 1. async/await 으로 작성
async function pickFruits() {
  const apple = await getApple();
  const peach = await getPeach();
  return `${apple} + ${peach}`;
}
pickFruits().then(console.log);
// 🍎 + 🍑 출력


	// 하지만 이처럼 사과, 복숭아의 delay 가 동일한 경우, 서로를 기다리는 게 무의미함.
	// promise 를 만들어 생성과 동시에 실행시켜 주면 더 빠르게 출력 가능해짐.
	// 2. 이처럼 병렬적으로 실행될 땐 Promise.all() 사용하면 됨
	// 다른 예로, Promise.race 를 하면 먼저 출력되는 하나만 출력하게 할 수 있음.
function pickAllFruits(){
  return Promise.all([getApple(), getPeach()])
    .then(fruits => fruits.join(' + '));
}
pickAllFruits().then(console.log);
// 🍎 + 🍑 출력

async/await 에서의 에러 처리

  • 에러처리는 try, catch를 사용하여 한다.
  • 유저에게 에러 메세지를 표시해야 하는 경우(예: 컨트롤러에서 코드 성공 여부를 클라이언트에게 알려줘야 하는 경우)에 사용한다.
export const postContents = async(req, res) => {
    const { title, description, hashtags } = req.body;
    
    try {
        await Contents.create({
            title,
            description,
            hashtags,
        });
        return res.redirect("/");
    } catch (error) {
        return res.status(400).render("upload", {
            pageTitle: "Upload Contents",
            errorMessage: error._message,
        });
    }
};
profile
좋은 개발자, 좋은 사람

0개의 댓글