자바스크립트 기초 - 비동기처리, 에러처리, 콜백헬

Minhyeok Kim·2022년 7월 27일
0
post-thumbnail

[17장 프로미스]

프로미스
자바스크립트는 비동기 처리를 위한 하나의 패턴인 콜백함수를 사용한다. 하지만 전통적인 콜백 패턴은 콜백헬로인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란하며 여러개의 비동기처리를 한번에 처리하는 데도 한계가 있다.

-콜백헬
비동기 처리 결과에 대한 후속처리를 수행하는 비동기 함수가 결과를 가지고 또 다시 비동기 함수를 호출하면서 호출이 중첩되어 복잡도가 높아지는 현상

-콜백 패턴 에러 처리의 한계
setTimeout 함수에서 에러가 나더라도 코드상의 에러가 나지 않는다.

그래서 나온게 프로미스(Promise)를 도입했다.
프로미스는 비동기 처리를 수행할 콜백함수를 인수로 전달받는데 이 콜백함수는 resolve 와 reject 함수를 인수로 전달받는다.

// 프로미스 생성
const promise = new Promise((resolve, reject) => {
    if(/* 비동기 처리 성공 */){
        resolve("result");
        // 성공했을 경우 이걸 출력
    }
    else {
        reject("failure reason");
        // 실패했을 경우 이걸 출력
    }
    }
});

// Promise 예제 2
const fulfilled = new Promise((resolve, reject) => {
  setTimeout(() => {
    if (Math.random() > 0.5) {
      resolve(1);
    } else reject(2);
  }, 500);
})
  .then((res) => console.log(res))
  // then 은 resolve 가 되었을 때 작동
  .catch((err) => console.log(err))
  // catch 는 reject 되었을 때 작동
  .finally(() => console.log("finally"))
  .catch(() => console.log("catch"))
  .then(() => console.log("then"));

// 프로미스 예제2
function runInDelay(seconds) {
  // 반복적으로 써야하는 콜백함수를 프로미스를 활용하여 축약
  return new Promise((resolve, reject) => {
    // 프로미스는 잘 되었을때, 실패했을 때를 구문해서 함수로 인수를 받아
    if (!seconds || seconds < 0) {
      // 부정을 활용하여 if 문의 true항을 error 출력문으로
      reject(new Error("Seconds 값이 이상합니다"));
    } else {
      // false 항을 성공case로 출력
      setTimeout(resolve, seconds * 1000);
    }
  });
}

// 콜백함수를 중복으로 사용하지 않고 시간만 인수로 받는 함수
runInDelay(-1)
  .then(() => console.log("good"))
  .catch(() => console.log("bad"))
  .finally(() => console.log("end"));
// Promise 의 메서드 then, catch, finally 는 콜백 함수를 인수로 받아야
// 하기 때문에 ()를 활용하여 마무리

프로미스 후속 처리 메서드
비동기 처리 상태가 변화 하면 이에 따른 후속 처리를 해야한다. (then, catch, finally 제공)
프로미스의 후속처리 메서드는 태스크 큐보다 우선순위가 높은 마이크로태스크 큐로 옮겨지기 때문에 먼저 실행된다.

Promise.prototype.then // resolve 상태일때
Promise.prototype.catch // reject 상태일때
Promise.prototype.finally // 성공실패와 상관없이 무조건

프로미스 정적 메서드
이미 존재하는 값을 매핑하여 프로미스를 생성하기 위해 사용
// 배열을 resolve 하는 프로미스를 생성
Promise.resolve
Promise.reject
Promise.all
Promise.allSettled

const requestData1 = () =>
  // 화살표 함수로 리턴
  new Promise((resolve) => setTimeout(() => resolve(1), 1000));
// promise를 만들었고, resolve 를 인수로 받아서, setTimeout일 경우,
const requestData2 = () =>
  new Promise((resolve) => setTimeout(() => resolve(2), 500));
const requestData3 = () =>
  new Promise((resolve) => setTimeout(() => resolve(3), 500));

Promise.all([requestData1(), requestData2(), requestData3()])
  .then(console.log)
  .catch(console.log.error);

// 풀어서 쓸 경우,
// const res = [];
// // 빈 배열이 하나 있고 여기다가 push 할거야
// requestData1()
//   .then((data) => {
//     res.push(data);
//     return requestData2();
//   })
//   .then((data) => {
//     res.push(data);
//     return requestData3();
//   })
//   .then((data) => {
//     res.push(data);
//     console.log(res);
//   })
//   .catch(console.log.error);

쭉 예제를 통해서 한번 익혀보자

// 콜백 함수
const waitCount = (callback) => {
  console.log("ONE");
  setTimeout(() => {
    console.log("TWO");
    callback();
  }, 1000);
};

const count3 = () => console.log("THREE");
waitCount(count3);

// 예제2 question,
function buyCoffee(coffeName, price, quantity) {
  console.log(`손님: ${coffeName}${quantity}개 구입하려 합니다.`);
  console.log(`알바생: ${coffeName} 한개는 ${price}원 입니다.`);

  setTimeout(() => {
    console.log("알바생: 커피값계산 완료했습니다.");
    return price * quantity;
  }, 1000);
}

function pay(money) {
  console.log(money + "원 여기있습니다.");
}

const latte = buyCoffee("라떼", 5000, 2);
pay(latte);
// => 가격이 undefined로 출력됨

// callback을 이용한 문제해결
function buyCoffee(coffeName, price, quantity, callback) {
  console.log(`손님: ${coffeName}${quantity}개 구입하려 합니다.`);
  console.log(`알바생: ${coffeName} 한개는 ${price}원 입니다.`);

  setTimeout(() => {
    callback(price * quantity);
    console.log("알바생: 커피값계산 완료했습니다.");
  }, 1000);
}

function pay(money) {
  console.log(money + "원 여기있습니다.");
}

const latte = buyCoffee("라떼", 5000, 2, pay);

// 프로미스 기본, 기명함수를 이용한 콜백함수의 지옥탈출
new Promise((resolve) => {
  setTimeout(() => {
    let name = "삼양라면";
    console.log(name);
    resolve(name);
  }, 500);
})

  .then((prevName) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        let name = prevName + ", 신라면";
        console.log(name);
        resolve(name);
      }, 500);
    });
  })
  .then((prevName) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        let name = prevName + ", 진라면";
        console.log(name);
        resolve(name);
      }, 500);
    });
  })
  .then((prevName) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        let name = prevName + ", 너구리";
        console.log(name);
        resolve(name);
      }, 500);
    });
  });

// 프로미스 간단한 방법
const addRamen = (name) => {
  return (prevName) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        const newName = prevName ? `${prevName}, ${name}` : name;
        console.log(newName);
        resolve(newName);
      }, 500);
    });
  };
};

addRamen("삼양라면")()
  .then(addRamen("신라면"))
  .then(addRamen("진라면"))
  .then(addRamen("너구리"))
  .then(addRamen("짜파게티"));

// 치킨공장 예제
function fetchEgg(chicken) {
  // chicken 을 인수로 가져오는 fetchEgg 함수
  // 참일때, chicken 인수를 넣은 문구를 출력
  return Promise.resolve(`${chicken} => ⚪`);
}
function getChicken(egg) {
  // egg 을 인수로 가져오는 getChicken 함수
  // 참일때, egg 인수를 넣은 문구를 출력
  return Promise.resolve(`${egg} => ☢`);
}
function getFarm(chicken2) {
  // chicken2 을 인수로 가져오는 getFarm 함수
  // 참일때, chicken2 인수를 넣은 문구를 출력
  return Promise.resolve(`${chicken2} => 🐔🐔`);
}

fetchEgg("🐔")
  // fetchEgg 함수를 호출시키고 인수를 넣어서 값을 얻은뒤
  // 아래 then에 값을 인수로전달
  .then((egg) => {
    // 그리고 나서 위서 나온 값을 egg 인수로 받아서
    return getChicken(egg);
    // getChicken 함수에 인수로 넣고 출력해 그리고 그다음 then에 전달
  })
  .then((chicken2) => {
    // 그리고 나서 위에서 나온 값을 chicken2 인수로 받아서
    return getFarm(chicken2);
    // getFarm 함수에 인수로 넣고 출력해 그리고 그다음 then에 전달
  })
  .then((chickenFarming) => console.log(chickenFarming));
// 인수로 받고 나서 출력해
// 사실 이부분에서 네이밍은 같은 수식안에서 같기만 한다면
// 아무거나 넣어도 돼
// 왜냐하면 중요한건 return을 어떻게 하느냐 이니까

// 출력과정 심플하게 만들기
// 바로 return 하는 경우 과정을 생략하고
// 받는 인수와 넣는 인수가 같으면 함수만 쓰면돼
fetchEgg("🐔") //
  .then(getChicken)
  .then(getFarm)
  .then(console.log);

// 프로미스 병렬처리
function getBurger() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("🍔");
    }, 1500);
  });
}

function getPizza() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("🍕");
    }, 1000);
  });
}

function getChicken() {
  return Promise.reject(new Error("We dont have it go away"));
}

// 햄버거와 피자를 함께 가지고 와서 배열에다가 간직하기
getBurger() // getBurger 함수를 가져와서
  .then(
    (
      burger // 출력하면서
    ) =>
      getPizza(burger) //바로 getPizza 함수를 가져와서
        .then((pizza) => {
          return [burger, pizza];
          // 출력하는데 return 으로 배열안에다가 값을 집어 넣어
        })
  ) //
  .then(console.log);

// Promise.all 병력적으로 한번에 모든 Promise 들을 실행
// 그렇기 때문에 합산 시간이 아닌 가장 오래걸리는 시간만 걸림
Promise.all([getBurger(), getPizza()]).then((foods) => {
  // 배열로 함수를 받아서 실행시키는것,
  console.log("All: ", foods);
});
// race 함수는 가장 빠르게 출력이 되는 것만 가져옴
Promise.race([getBurger(), getPizza()]).then((firstIn) => {
  console.log("First: ", firstIn);
});

[18장 async/await]

에이싱크/어웨이트 ㅋ
프로미스를 기반으로 동작한다.
프로미스를 후속처리 메서드없이 마치 동기처럼 결과를 반환하도록 구현할 수 있다.

async 함수
await 키워드는 반드시 async 함수 내부에서 사용해야 한다.
async 함수는 암묵적으로 반환값을 resolve 하는 프로미스를 반환한다.

await 키워드
프로미스가 비동기 처리가 수행된 상태(settled)가 될 때까지 대기하다가 settled상태가 되면 프로미스가 resolve 한 처리 결과를 반환한다.

에러처리
async/await에서 에러처리는 try...catch문을 사용할 수 있다.
async 함수 내에서 catch문을 사용해서 에러 처리를 하지 않으면 async 함수는 발생한 에러를 reject하는 프로미스를 반환한다.

function getBurger() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("🍔");
    }, 500);
  });
}

function getPizza() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("🍕");
    }, 700);
  });
}

function getChicken() {
  return Promise.reject(new Error("We dont have it go away"));
}

// async/await 를 활용하여 출력과정 간소화
// 하나 하나 불러와서 꼬리를 무는 형식이아니라
// 망라하는 함수 하나를 만들어서 각 프로미스의 인수를 정의하고
// 정의된변수를 배열로 리턴한다.
// 이때 변수속 함수들은 await 당한다
async function getFoods() {
  const burger = await getBurger();
  const pizza = await getPizza();
  return [burger, pizza];
}
// 망라하는 함수를 호출하고, then과 인수를 이용하여 출력한다.
getFoods().then((foods) => {
  console.log(foods);
});

[19장 HTML 기초]

HTML (Hyper Text Markup Language) 기본 틀 담당
CSS (Cascading Style Sheets) 정적 처리 담당
js (Java Script) 동적 처리 담당

... to be continue...

0개의 댓글