동기(Sync) vs 비동기 (Async)

YunKuk Park·2022년 2월 13일
24

👣 하나씩 꾸준히

목록 보기
13/15

Javascript 는 싱글쓰레드 이지만, 그래도 쓰레드의 개념을 알고 오면 좋다.

tl; dr

비동기 : 짬때리는 행위

Callback : 이거 했을 때 이거 해줘!

근데 이거 너무 길어지면 보기 힘드니까 Promise 가 생김

Promise : 약속할게 데이터 준다고...

Async : return 값으로 Promise 줄게

Await : Async function 내에서 이거 될 때까지 내 밑에 기다려! 하고싶을 때

보통 setTimeout 을 사용하여 비동기처리 예시를 드는데 처음 배웠을 때 실제 사용방법과 약간의 차이점이 있어서 잘 이해하지 못했던 부분이 있었다.
그래서 여기서는 Coin paprika api 에서 coin 정보들을 fetch하는 것을 예시로 들 예정이다.

Callback

💡
정의 :: 함수에 파라미터로 들어가는 함수
용도 :: ...할 때! ⇒ 이거 해라 같이 순차적으로 실행하고 싶을 때 씀

I.Promise

Callback 이 너무 심하게 들어가는 걸 방지하기 위해 태어난 Promise

💡 두가지의 키워드!!
State :: process 실행 중 || 성공 || 실패
Producer , Consumer :: 정보 제공자와 사용자

state

pendingfulfilled || rejected

Promise 만들기

const promise = new Promise()

새로운 promise 가 만들어 질 때에는 우리가 생성한 executor 함수가 바로 실행된다.

const promise = new Promise((resolve, reject) => {
  // 뭔가 헤비한 일들 (데이터를 가지고 오거나, 큰 데이터를 읽어올 때)
	// resolve 안에 우리가 비동기적으로 해야 하는 일을 넣어준다.
  resolve(fetch('https://api.coinpaprika.com/v1/coins').then(res=>res.json()));
});

Promise 사용하기

promise.then((data) => console.log(data))

기타 에러 핸들링

Producer 쪽에서는 에러가 발생 했을 때 해주고 싶은 일을 reject 에 적어주고

Consumer 쪽에서는 .catch 를 이용하여 reject 된 정보를 가져온다.

Consumer.finallyresolve 되거나 reject 되거나에 상관 없이 실행된다.

const promise = new Promise((resolve, reject) => {
  // 뭔가 헤비한 일들 (데이터를 가지고 오거나, 큰 데이터를 읽어올 때)
  resolve(
    fetch('https://api.coinpaprika.com/v1/coins').then((res) =>
      res.json()
    )
  );
  reject(new Error('no network'));
});

promise
  .then((data) => console.log(data.slice(0, 100)))
  .catch((err) => console.log(err))
  .finally(() => console.log('아 시원하다 끝났다'));

Promise Chaning

fetchCoins
  .then((data) => data.slice(0, 100))
  .then((data) => data.map((item) => item.name))
  .then(
    (names) =>
      new Promise((resolve, reject) => {
        resolve(names.slice(0, 30));
      })
  )
  .then((data) => console.log(data));

.then 에서는 값을 바로 전달해도 되고 새로운 promise 를 전달해도 된다.

오류 처리

const getHen = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve('🐓'), 500);
  });

const getEgg = (hen) =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${hen} => 🥚`), 500);
  });

const cook = (egg) =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${egg} => 🍳`), 800);
  });

getHen()
  .then(getEgg)  // .then(hen => getEgg(hen))
  .then(cook)    // .then(egg => cook(egg))
  .then(console.log); // .then(result => console.log(result)) 
                      // 이런식으로 하나의 인자가 들어가고 함수가 하나의 인자를 받으면 callback 함수 데려오기만 해도 됌
// 🐓 => 🥚 => 🍳

getHen() //
  .then(getEgg)
  .catch((err) => '🧐')
  .then(cook)
  .then(console.log);

// 에러가 났을 경우 다음과 같이 핸들링 ::  🧐 => 🍳

II. Async Await

💡
Promise Chain 을 더 간결하고 동기적으로 실행되는 것처럼 보이게 만들어주고 싶었다.

깔끔하게 Promise 를 사용 할 수 있는 방법

단, async await 가 무조건 좋다 ❌ 
Promise 와 적절히 섞어 써야한다.

async

동기적 이렇게 만들면?

const fetchCoins = () => {
  return fetch('https://api.coinpaprika.com/v1/coins').then((res) => res.json());
};

const data = fetchCoins();
console.log(data);  // undefined

undefined 가 나온다.

그리고 network 패널에 들어가보면 데이터가 페칭되어 있다.

엥?? undefined 떳었잖아!!!

함수 앞에 async 를 붙히자

const fetchCoins = async () => {
  return fetch('https://api.coinpaprika.com/v1/coins').then((res) =>
    res.json()
  );
};

const data = fetchCoins();
console.log(data);

콘솔을 찍어보면 Promise 가 나오고 resultreturn 된 값이 나온다.

💡
async 를 함수 앞에 붙혀주면 자동으로 해당 함수는 Promiseresolve 를 뱉어내는 함수가 된다.

await

💡
async 가 붙은 함수 안에서만 쓸 수 있다.

const fetchCoins = async () => {
  const data = await fetch('https://api.coinpaprika.com/v1/coins').then((res) =>
    res.json()
  );

  return data;
};

fetchCoins().then(console.log);

💡
await 의 강점은! promise 안에서의 콜백지옥 같은 녀석을 이쁘게 처리 해주는 것!

// await 를 쓰지 않고 Promise Chaining했을 경우
// 물론 Promise.all 을 써도 좋음 (사용 방법에 따른 차이)
const getAll = () => {
  return fetchCoins().then((coins) => {
    return fetchMarketOverview().then((global) => { coins, global });
  });
};

getAll().then(console.log);

// await 를 사용 한 경우
const getAll = async () => {
  const coins = await fetchCoins();
  const overview = await fetchMarketOverview();
  return { coins, overview };
};

getAll().then(console.log);

문제점

await 병렬 처리

순차적으로 진행할 때 비효율 적일수 있다.

위의 예시에서 coins 의 정보를 받아오는 것과 overview 정보를 받아오는 것이 직렬적으로 처리 되고 있고,
둘은 서로 동시성으로 처리 해도 되는 것이기 때문에 비효율적이다..

const getAll = async () => {
  const coinsPromise = fetchCoins();
  const overviewPromise = fetchMarketOverview();
  const coins = await coinsPromise;
  const overview = await overviewPromise;
  return { coins, overview };
};

이런식으로 Promise 를 바로 만들어서 ‘동시성' 으로 fetch 되게 하고 그 내용을 await 하여
병렬 처리를 할 수 있다.

💡
그런데 이거 너무 지저분한데요??

useful Promise APIs

⭐️ Promise.all 여러군데(여러 엔드포인트)에서 한번에 fetch 받아올 때 많이 씀

const getAll = () => {
  return Promise.all([fetchCoins(), fetchMarketOverview()]).then((data) => {
    const [coins, overview] = data;
    return { coins, overview };
  });
};

getAll().then(console.log);

Promise.race

먼저 전달된 내용만 가져가겠다.

const pickOnlyOne = () => {
  return Promise.race([fetchCoins(), fetchMarketOverview()]).then(
    (data) => data
  );
};

리팩토링

const COIN_URL = 'https://api.coinpaprika.com/v1/coins';
const GLOBAL_URL = 'https://api.coinpaprika.com/v1/global';

const getData = async (opts) => {
  const fetchData = await fetch(opts.url);
  return fetchData.json();
};

const getAll = () => {
  return Promise.all([getData({ url: COIN_URL }), getData({ url: GLOBAL_URL })]) //
    .then((data) => {
      const [coins, overview] = data;
      return { coins, overview };
    });
};

getAll().then(console.log);

마지막 정리

프로미스스님 한판해요

Promise 핵심

  • rule1. Promise를 쓴다는 말은 곧 Promise객체를 return 하는 것과 같은 말이다.
  • rule2. then에 전달되는 함수가 받는 parameter는 resolve에 넣은 value가 전달된다.
  • rule3. Promise를 리턴하는 함수를 실행하게 되면 리턴값은 당연히 Promise 객체가 즉시 리턴된다. 이 Promise 객체가 pending, fulfilled, rejected되는 것은 그 다음 문제이다.
  • rule4. 중요: new Promise() 를 하게 되면 즉시 Promise에 전달 된 executor 함수가 실행된다.
  • rule5. 마찬가지로, Promise를 리턴하는 함수를 실행하게 되면 바로 Promise에 전달 된 executor 함수가 즉시 실행된다.
  • rule6. Promise chaining을 하게 되면 then()에서 return한 값을 그 다음 then()에 전달되는 함수(executor)의 parameter로 들어 가게 된다.
  • rule7. Promise chaining을 할 때 then은 value를 바로 return해도 되며, promise를 return 해도 된다. value를 리턴 할 경우엔 즉시 그 다음 then에 전달한 function이 즉시 실행되며, promise를 return 할 경우 해당 promise가 resolve되면 그 다음 then이 실행된다.
  • rule8. 동시에 여러 Promise를 실행 시킬 때는 Promise.all()을 자주 사용한다.

Async/Await 핵심

  • rule1. async function을 만들면 javascript/typescript 내부적으로는 자동으로 Promise 객체를 리턴하게 된다. 즉, 자동으로 Promise를 return해 주게 된다.
  • rule2. async function을 await 없이 실행하면 Promise를 return 한다. await가 없기 때문에 기다림 없이 Promise를 즉시 리턴받게 된다. (위 룰 4, 5와 동일)
  • rule3. 위 rule2를 사용하는 사람도 있겠지만 왠만하면 Promise.all()을 이용해서 배열화 시킨다.

REF.

드림코딩 엘리 비동기 처리 관련

profile
( • .̮ •)◞⸒⸒

0개의 댓글