[javascript] 동기와 비동기

Uhan33·2024년 1월 4일
0

TIL

목록 보기
7/72

동기 VS 비동기

동기적 처리와 비동기적 처리는 javascript를 배우다 보면 자연스럽게 접하게 된다.
오늘은 동기와 비동기가 무엇인지, 차이점은 뭔지 알아보려고 한다.

출처 : https://smallzoodevs-organization.gitbook.io/copy-of-javascript-study/day-05./1.

한눈에 이해되는 그림이 있어서 가지고 왔다.

  • 그림에서 위를 보면 손님이 주문을 하고서 커피가 나올 때까지 그 자리에서 기다리고 있다.
    하나의 요청이 들어가면 그 요청이 끝날 때 까지 기다려야 하는 것이다. 뒷 사람들의 표정이 좋지 않아 보인다..
  • 그림의 아래를 보면 손님이 주문을 하고서 진동벨을 받고, 자신의 주문이 완료가 되면 커피를 가지러 간다.
    손님은 주문하는데 오래 기다리지 않아도 되고, 여러 주문이 들어와도 빠르게 작업이 가능한 것 부터 내보내기에 기다리는 시간 없이 먼저 완료된 주문부터 내보내게 되는 것이다.

일상생활에서 보는 동기와 비동기이다. 느낌이 딱 오는 것 같다.
이제는 실제 컴퓨터에서 어떤 작업을 말하는지 알아보자.

동기(synchronous)

  • 현재 실행중인 코드가 끝나야 다음 코드가 실행되는 방식이다.
  • CPU의 계산에 의해 즉시 처리가 가능한 대부분의 코드는 동기적 코드이며, 계산이 복잡해서 CPU가 계산하는 데에 오래 걸리는 코드 역시도 동기적 코드이다.

비동기(asynchronous)

  • 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식이다.
  • 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 모두 비동기적 코드이다.
  • setTimeout, addEventListner 등이 있다.

우리가 가볍게 쓰는 코드들은 대부분 동기적이라고 보면 된다.

비동기 작업은 굉장이 효율적으로 보인다. 하지만 문제도 있다.
만약 a, b의 작업이 있는데 a의 작업의 결과를 기반으로 b의 작업을 해야한다면?
비동기적으로 했을 때 a가 b보다 빨리 끝난다면 괜찮겠지만,
a가 어떠한 이유로 b보다 작업이 늦게 끝나게 된다면.. b의 작업은 망가지게 된다.
그래서 비동기 작업에도 동기적 표현이 필요하다.

비동기 작업의 동기적 표현

크게 세 가지의 방법이 있다.

  1. Promise
  2. Generator
  3. async / await

1. Promise

Promise는 비동기 처리에 대해, 처리가 끝나면 알려달라는 ‘약속’이다.
new 연산자로 호출한 Promise의 인자로 넘어가는 콜백은 바로 실행되며,
그 내부의 resolve(또는 reject) 함수를 호출하는 구문이 있을 경우 resolve(또는
reject) 둘 중 하나가 실행되기 전까지는 다음(then), 오류(catch)로 넘어가지 않는다.
따라서, 비동기작업이 완료될 때 비로소 resolve, reject이 호출된다.

let myFirstPromise = new Promise((resolve, reject) => {
  // 우리가 수행한 비동기 작업이 성공한 경우 resolve(...)를 호출하고, 
  // 실패한 경우 reject(...)를 호출한다.
  // 이 예제에서는 setTimeout()을 사용해 비동기 코드를 흉내낸다.
  setTimeout(function () {
    resolve("성공!"); // 문제 없음!
  }, 250);
});
myFirstPromise.then((successMessage) => {
  // successMessage는 위에서 resolve(...) 호출에 제공한 값.
  // 위에서 문자열을 줬으니 아마 문자열일 것이다.
  console.log("와! " + successMessage); // 와! 성공!
});

2. Generator

*가 붙은 함수가 제너레이터 함수이다.
제너레이터 함수는 실행하면, Iterator(반복이라고 생각하자.) 객체가 반환(next()를 가지고 있음)된다.
next 메서드 호출시, Generator 함수 내부에서 가장 먼저 등장하는 yield 에서 stop.
이후 다시 next 메서드를 호출하면 멈췄던 부분 -> 그 다음의 yield 까지 실행 후 stop한다.
즉 ,비동기 작업이 완료되는 시점마다 next 메서드를 호출해주면 Generator 함수 내부소스가 위 -> 아래 순차적으로 진행된다.

function* generator() {
  yield 1;
  yield 2;
  yield 3;
}
const gen = generator(); // "Generator { }"
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3

3. async / await

ES2017에서 새롭게 추가된 async/await 문이다.
비동기 작업을 수행하고자 하는함수 앞에 async를
함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 붙여주면 된다.
암시적으로 Promise를 사용하여 결과를 반환한다.

function resolveAfter2Seconds() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}
async function asyncCall() {
  console.log('calling');
  const result = await resolveAfter2Seconds();
  console.log(result);  // Expected output: "resolved"
}
asyncCall();
  • Promise를 async/await으로 리팩토링

promise를 사용한 코드를 async를 사용한 코드로 바꿔줄 수 있다. 아래 예시를 보자.

function getProcessedData(url) {
  return downloadData(url) // returns a promise
    .catch((e) => {
      return downloadFallbackData(url); // returns a promise
    })
    .then((v) => {
      return processDataInWorker(v); // returns a promise
    });
}

위 코드는 아래로 변경이 가능하다.

async function getProcessedData(url) {
  let v;
  try {
    v = await downloadData(url);
  } catch (e) {
    v = await downloadFallbackData(url);
  }
  return processDataInWorker(v);
}

위 예제에서는 return 구문에 await 구문이 없는데,
이는 async function의 반환값이 암묵적으로 Promise.resolve로 감싸지기 때문이다.

마치며..

이번에 콜백 함수를 배우면서 동기 비동기를 배웠는데, 이전에 nodejs로 프로젝트를 하면서 async / await를 많이 사용했었다. (어떻게 동작하는지도 모르고..)
사실 100프로 이해했다고는 못하지만 머릿속에 정리가 되는 느낌이다.
어렵지만.. 나 화이팅...

0개의 댓글