Javascript 동기&비동기

hong·2022년 5월 3일
0
post-thumbnail
post-custom-banner

🔎 동기(Synchronous)란?

동시에 일어난다는 의미. 요청을 보낸 후 해당 요청의 응답을 받아야 다음 동작을 실행하는 방식

🔎 비동기(Asynchronous)란?

동시에 일어나지 않는다는 의미. 요청을 보낸 후 응답과 관계없이 다음 동작을 실행하는 방식

  • 커피 주문을 예로 들어보자 ☕️
    • 당신이 카페에서 커피를 주문하려고 할 때, 점원이 한 명 뿐이라면 당신을 포함한 손님들은 점원이 맨 앞 손님의 주문을 받고, 음료를 만들고, 계산하는 것까지 기다려야한다. 마치 Queue처럼 선입선출 과정의 코드 처리 순서를 동기 방식이라고 부른다.

      여기서 여러 명의 점원이 있다고 생각해보자. 이 점원들은 주문을 받은 순서대로 일을 처리하는 것이 아니라 각자 파트대로 음료를 나누어 먼저 완성되는 쪽이 음료를 내놓는다. A, B 순서로 주문을 했더라도 B의 음료가 먼저 완성되면 주문한 순서와 관계없이 B가 먼저 음료를 가져갈 수 있는 것이다. 여기서 점원들은 비동기 방식 으로 수행했다고 할 수 있다.


💡 상황에 따른 동기/비동기 장단점

✔️  동기 방식
장점 : 설계 매우 간단. 직관적

단점: 결과가 주어질때까지 아무것도 하지 못하고 대기

✔️  비동기 방식
장점: 결과가 주어지는데 시간이 오래 걸리더라도 그 시간 동안 다른 작업을 할 수 있음. 자원을 효율적으로 사용할 수 있음
단점: 동기보다 설계 복잡


❓ setTimeout

setTimeout은 JavaScript의 전형적인 비동기 작업이다.

function f1(){
  setTimeout(() => {
    console.log(1);
  }, 2000);  // watiting for 2s
}

function f2() {
  setTimeout(() => {
    console.log(2);
  }, 1000);  // waiting for 1s
}


f1();
f2();
// result: 2
// 	       1

f1()은 2초 뒤에 1이 출력되고, f2()는 1초 뒤에 2가 출력된다.
f1()이 먼저 호출됐기 때문에 1 2 라는 결과를예상할 수도 있지만, JavaScript는 비동기 방식으로 처리되기 때문에 2 1 이 출력될 것이다.

그렇다면, 이를 순차적으로 출력하려면 어떻게 해야 할까?

setTimeout(() => {  // f1()
  console.log(1);
  setTimeout(() => {  // f2()
    console.log(2);
    setTimeout(() => {
      console.log(3);
      //...
    }, 1000)
  }, 1000)
}, 1000)
// result: 1
//         2
//         3

순차적으로 출력되지만, Callback Hell의 형태처럼 코드가 점점 길어진다.

// Callback Hell 예시
// Callback Hell 은 비동기 프로그래밍시 발생하는 문제이다.
function Callback(callback) {
  function Callback2(callback2) {
    function Callback3(callback3) {
      //...
    }
  }
}

JavaScript는 비동기 처리를 위하여 하나의 패턴으로 콜백 함수를 사용한다.
하지만 전통적인 콜백 패턴은 Callback Hell로 인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란하다.


❓ Promise

Promise 는 Callback Hell을 보완하며 비동기 처리 시점을 명확하게 표현한다. 주로 서버에서 받아온 데이터를 표시할 때 사용한다.

new Promise((resolve, reject) => {
  // ...
})
.then(() => {
  // ...
})
.catch(() => {
  // ...
})

Promise 내부에 익명 함수를 호출할 수 있으며, 인자는 resolvereject 를 받는다. Promise 는 체이닝 기법을 통해 thencatch 를 호출할 수 있다.

익명 함수는 무조건 실행되며, 내부 작업이 성공적으로 처리 되었을 때 resolve 를 호출하여 then 으로 분기시킬 수 있다. 만약 처리가 비정상적으로 되었다면 reject 를 호출하여 catch 로 분기해 에러를 출력하는 쪽으로 빠질 수 있다.

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('멍');
  }, 1000);
})
.then(result => {
  console.log(result + '!');
  return result + result;
})
.then(result => {
  console.log(result + '!'); 
  return result + result;
})
.then(result => {
  console.log(result + '!'); 
});
// result: 멍!
//         멍멍!
//         멍멍멍

이 1초에 한 줄씩 출력된다.


❓ async/await

ES8에서는 async await 를 지원한다. Promise 로 제공하던 함수들을 더 간결하고 직관적으로 실행할 수 있다.

async function getInfo() {
  let chiwawa = await db.collection('강아지').doc('몰키').get();
  console.log(chiwawa.data());  // await 다음에 실행됨

위 코드는 firestore 에서 강아지 정보를 가져오는 비동기 함수 예시이다. function 앞에 async 가 쓰여진 함수가 실행 중에 await 라는 코드를 만나면 코드의 진행이 멈추고 await 코드가 끝난 뒤에 그 다음 작업을 실행하게 된다.await 를 기다렸다가 정보를 받은 후 아래의 console.log 코드를 실행하게 되는 것이다. 만약 await 를 붙이지 않는다면 데이터를 받아오기 전에 console.log 넘어가고, 콘솔에는 undefined 가 출력될 것이다.

profile
🐶 ☕️ 🧳
post-custom-banner

0개의 댓글