javascript_Callback & Promise / Async & Await

장봄·2020년 6월 29일
1

code-states_IM_4주차

목록 보기
1/3

비동기 처리의 시작 Callback

javascript는 동기적으로 실행된다. 동기적으로 실행된다는 의미는 코딩되어 있는 순서대로 읽고 실행된다는 의미이다. 동기적으로 이용하게되면 처음 코드가 실행이 되는 시간동안 다음 행동으로 넘어가지 못하고 모든 행동이 끝날때까지 기다렸다가 끝이나면 다음 코드를 실행하는 방법이라서 많이 시간을 기다려야하는 단점이 있다. 그래서 동기적으로 코드를 실행해야하는 경우가 아니면 비동기를 이용해서 같은 시간에 많은 코드가 실행이 될 수 있는 비동기를 이용한다.

비동기적으로 코드를 실행시키기 위한 방법 중 하나가 callback를 사용한다. 물론 callback에는 동기적으로 실행되는 경우도 있다. callback을 비동기적으로 이용하는 경우에는 파라미터로 함수를 전달하고 함수 실행시 콜백함수를 실행할 수 있다. 이렇게 비동기적으로 실행을 하면 처음 코드가 실행이 되는 동안 다음 코드를 실행하여 여러 업무을 한번에 처리되는 장점이 있다. 하지만 콜백함수가 너무 많아서 콜백함수내부에 콜백함수가 있는 과정이 많아지면 이를 콜백지옥이라고 한다.

Promise

callback함수로 비동기함수를 만드는 경우 너무 많은 callback으로 콜백지옥이 형성되기도 하고 함수실행을 순차적으로 실행되지 않아서 가독성이 떨어지는 단점이 있다. 이를 보안하기 위한 것이 Promise이다.

Promise는 javascript에서 제공하는 비동기를 간편하게 처리할 수 있는 Object이다. Promise에는 State와 Producer와 Consumer가 있다. 먼저 State는 의미를 해석하면 상태이다. 코드를 실행했을때 성공했는지 실패했는지 아니면 아직 실행중에 있는지 알려주는 것이다. 결과는 실행중인 경우에는 pending, 성공시 fullfilled, 실패시 rejected로 알려준다. Producer는 원하는 기능을 수행해서 해당하는 데이터를 만든다. Consumer은 then, catch, finally 등으로 값을 처리한다. 그리고 새로운 Promise가 만들어지면 우리가 전달한 함수가 자동으로 즉시실행이 된다.

코드 예시를 보면 기능을 수행해서 성공적으로 데이터를 호출한 경우 resolve라는 함수를 호출한다. 네트워크나 파일에서 받아온 정보를 가공해서 resolve콜백함수를 통해서 전달하면 된다. 아래의 코드는 data를 받아왔다고 가정하고 코드를 작성해준다.

const promise = new Promise((resolve, reject) => {
  console.log("doing somthing");
  setTimeout(() => {
    resolve("spring");
  }, 1000);
});

그런다음 then으로 콜백함수를 넣어서 처리한다.

promise.then((value) => {
  console.log(value);
});
//spring이 console에 확인된다.

오류가 발생한 경우에는 아래와 같이 reject를 이용해서 에러가 발생하는 이유를 전달하는 코드를 작성한다.

const promise = new Promise((resolve, reject) => {
  console.log("doing somthing");
  setTimeout(() => {
    //resolve("spring");
    reject(new Error("에러가 발생하는 이유를 설명"))
  }, 1000);
});

그다음 아래와 같이 catch를 이용해서 처리한다.

promise
  .then((value) => {
    console.log(value);
  })
  .catch((error) => {
    console.log(error);
  });
//Error: "에러가 발생하는 이유를 설명"

이렇게 promise를 이용하는 기본적인 코드예시를 보았다. 추가적으로 finally는 성공이나 실패여부와 상관없이 마지막에 실행되는 함수로서 전달인자를 받지않고 콜백함수를 실행한다.

promise
  .then((value) => {
    console.log(value);
  })
  .catch((error) => {
    console.log(error);
  })
  .finally(() => {
    console.log("finally");
  });
//"finally"가 콘솔에 확인된다.

Promise chaning

const fetchNumber = new Promise((resolve, reject) => {
  setTimeout(() => resolve(1), 1000);
});

fetchNumber
  .then((num) => num * 2)
  .then((num) => num * 3)
  .then((num) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve(num - 1), 1000);
    });
  })
  .then((num) => console.log(num));

Async & Await

깔끔하게 rpomise를 사용할 수 있는 방법이다. 그렇다고 무조건 promise가 나쁘거나 Async & Await를 사용해야만 하는 것은 아니다. 상황에 따라 필요한 것을 적용하는 것이다.

함수앞에 Async만 붙여주면 함수를 Promise가 된다. 간편하게 Promise를 사용할 수 있다.

async function fetchUser() {
  return "spring";
}

const user = fetchUser();
user.then(console.log);

await를 사용하는 코드를 만들어봤다.

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function getApple() {
  await delay(1000);
  return "🍎";
}

async function getBanana() {
  await delay(1000);
  return "🍌";
}

async function pickFuites() {
  return getApple().then((apple) =>
    getBanana().then((banana) => `${apple} + ${banana}`)
  );
}

pickFruites().then(console.log);
//2초후에 🍎 + 🍌

delay되는 시간을 파라미터로 받는 Promise를 만드는 함수를 선언하고 delay함수를 이용해서 사과와 바나나를 얻는 함수를 선언한다. 그 다음 모든 과일을 받는 함수를 만들고 콘솔창에 띄운다. 이렇게 만든 함수는 callback함수에서 본 콜백지옥을 떠올리게 만들만한 함수가 되었다. 이것을 await를 이용해서 간단하게 만들면 아래와 같다.

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function getApple() {
  await delay(1000);
  return "🍎";
}

async function getBanana() {
  await delay(1000);
  return "🍌";
}

function pickFruites() {
    const apple = await getApple();
    const banana = await getBanana();
    return `${apple} + ${banana}`;
}

pickFruites().then(console.log);
//2초후에 🍎 + 🍌

이렇게 코드를 만들면 코드가 간단해지고 가독성도 좋아진다. 코드에 에러가 발생한 경우 try이와 catch를 통해서 에러처리도 가능하다.

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function getApple() {
  await delay(1000);
  throw "error";
  return "🍎";
}

async function getBanana() {
  await delay(1000);
  return "🍌";
}

async function pickFruites() {
  try {
    const apple = await getApple();
    const banana = await getBanana();
  } catch (er) {
    console.log(er);
  }

  return `${apple} + ${banana}`;
}

pickFruites().then(console.log);

여기서 더 생각을 해보면 사과와 바나나를 가져오는 함수를 서로 연관이 없기때문에 서로 기다릴 필요가 없다. 병렬적으로 각자의 함수가 실행되도록 코드를 만들면 1초만에 값이 return되게 할 수 있다.

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function getApple() {
  await delay(1000);
  throw "error";
  return "🍎";
}

async function getBanana() {
  await delay(1000);
  return "🍌";
}

async function pickFruites() {
  //새로운 Promise가 만들어지면 우리가 전달한 함수가 자동으로 즉시실행이 된다.
  const applePromise = getApple();
  const bananaPromise = getBanana();
  const apple = await applePromise;
  const banana = await bananaPromise;

  return `${apple} + ${banana}`;
}

pickFruites().then(console.log);
//1초만에 결과값이 리턴된다.

이렇게 병렬적으로 코드가 실행되어서 시간은 줄었지만 많은 코드를 작성하게 되는 단점이 있다. 이 단점을 보안하기 위해서 Promise에서 제공되는 api를 이용하면 간단하게 작성이 가능하다. 바로 promise의 all함수이다. promise배열을 전달하게 되면 병렬적으로 다 받을때까지 모아준다

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function getApple() {
  await delay(1000);
  throw "error";
  return "🍎";
}

async function getBanana() {
  await delay(1000);
  return "🍌";
}

function pickAllFruits() {
  return Promise.all([getApple(), getBanana()]).then((fruits) =>
    fruits.join(" + ")
  );
}

pickAllFruits().then(console.log);

이렇게 간단하게 all을 이용해서 배열로 만들고 마지막에는 join으로 문자열로 만들어서 콘솔에 출력한다.

profile
즐겁게 배우고 꾸준히 블로깅하는 개발자입니다 ;>

0개의 댓글