AR삽질러

JavaScript - 동기와 비동기(19) 본문

WEB/JavaScript

JavaScript - 동기와 비동기(19)

아랑팡팡 2024. 8. 25. 22:28
728x90

JavaScript - 동기와 비동기(19)

 

1. 동기(Synchronous)와 비동기(Asynchronous)

 - 동기와 비동기는 코드가 실행되는 방식을 설명하며 실행 흐름과 처리방식에 영향을 미치게된다.

console.log(1);
setTimeout(() => {
  console.log(2);
}, 3000);

console.log(3);

동기

 - console.log(2), console.log(3)은 순서대로 실행되는 동기작업

비동기

 - setTimeout(() => { console.log(2); }, 3000); 는 비동기 함수로 JavaScript엔진에 의해서 호출된 후 내부적으로 타이머가 설정된다.

 

2. 동기(Synchronous)

 - 동기 방식에서는 코드가 순차적으로 실행된다.

 - 하나의 작업이 완료되기 전까지는 다음 작업이 실행되지 않기 때문에 각 작업은 이전 작업이 끝날 때까지 기다린 후 실행되어 코드가 위에서 아래로 차례대로 실행된다.

 특징

 - 작업이 블로킹되어 이전 작업이 완료될 때까지 다음 작업이 실행되지 않는다.

 - 코드의 흐름이 예측 가능하다.

 단점

 - 동기 방식은 작업이 오래 걸리거나 시간이 지연되는 경우 전체 프로그램이 문제가 될수 있다. 예로 UI가 멈추가나 웹앱이 응답하지 않는 문제가 발생할 수 있다.

 

3. 비동기(Asynchronous)

 - 작업이 동시에 실행될 수 있도록 관리되며 특정 작업이 완료될 때까지 기다리지 않고 다음 작업을 바로 실행한다. 콜백함수나 Promise 등을 사용해 비동기 작업이 완료된 후에 동작을 정의한다.

 특징

 - 작업이 블로킹 되지 않는다. 특정 작업이 완료되기를 기다리지 않고 다음 작업이 즉시 실행된다.

 - 작업완료 후 처리. 비동기 작업이 완료되면 해당 작업의 결과를 처리한다.

 - 시간이 오래 걸리는 작업(네트워크 요청)등이 있을 경우 다른 작업이 먼저 실행될 수 있어 프로그램이 멈추가 않는다.

 

4. 비동기 - 콜백

 - 콜백 함수는 다른 함수에서 인수로 전달되는 함수로 비동기 작업이 완료되었을 때 해당 콜백 함수가 호출되며 비동기 작업이 끝난 이후 실행될 동작을 정의하는데 사용된다.

콜백함수의 특징 비동기 작업을 처리하기 위한 기본적인 방법
중첩된 콜백 함수를 사용할 수 있다.
콜백 함수가 중첩되면 콜백 지옥 문제가 발생한다.

 

function add(a, b, callback) {
  setTimeout(() => {
    const sum = a + b;
    callback(sum);
  }, 3000);
}

add(1, 2, (value) => {
  console.log(value);
});

add(a, b, callback)

 - a, b 에서 숫자를 받아 callback에서 두수를 더해 콜백 함수로 처리한다.

콜백 함수의 역할 

 - add함수가 호출될 때 세번째 인수로 전달되는 익명함수 (value) => { console.log(value)};가 콜백 함수로 사용되고 sum값을 인수로 받아 해당 값을 콘솔에 출력하는 역할

 

비동기 작업을 연속적으로 실행

function orderFood(callback) {
  setTimeout(() => {
    const food = "치킨";
    callback(food);
  }, 3000);
}

function deliveryFood(food, callback) {
  setTimeout(() => {
    const delivery = 주문하신 ${food} 이 배달되었습니다.;
    callback(delivery);
  }, 2000);
}

function review(food, callback) {
  setTimeout(() => {
    const reviewMessage = ${food} 은 맛있었나요? 리뷰를 작성해주세요!;
    callback(reviewMessage);
  }, 1500);
}

orderFood((food) => {
  console.log(food);

  deliveryFood(food, (delivery) => {
    console.log(delivery);

    review(delivery, (reviewMessage) => {
      console.log(reviewMessage);
    });
  });
});

orderFood(callback)

 - 3초뒤에 "치킨"을 준비하고 콜백함수로 전달해 출력한다.

 

deliveryFood(food, callback)

 - orderFood() 함수의 결과인 food를 받아 2초후 메시지를 생성하고 콜백함수로 전달한다.

 

review(food, callback)

 - deliveryFood() 함수의 결과인 배달 메시지를 받아 1.5초 후 메시지를 생성하고 콜백함수로 전달한다. 

 

비동기

orderFood(food)

 - 콜백으로 전달된 food 값을 받아 출력한후 deliveryFood() 함수가 실행된다.

deliveryFood()

 - 콜백에서 메시지를 출력한 후 review()함수가 실행된다.

 

4-1. 콜백지옥

 - 위 코드는 비동기 작업들이 연속적으로 실행되기 때문에 콜백 함수가 중첩되어있다. 이와 같은 상황을 콜백지옥이라고 부른다.

콜백지옥의 특징 중첩된 구조
복잡성 증가
가독성 문제

 

 

5. 비동기 - Promise

 - Promise는 비동기 작업을 감싸는 객체로 비동기 작업의 성공 또는 실패 여부를 표현하고 이를 처리하는 방식으로 콜백지옥 문제를 해결할 수 있다.

Promise의 특징 상태관리
체이닝
에러처리

 

Promise의 3가지 상태 대기(Pending) 아직 작업이 완료되지 않은 상태
성공(Fulfilled) 비동기 작업이 성공적으로 마무리 된 상태
실패(Rejected) 비동기 작업이 실패한 상태

 

Promise 체이닝

 - 여러 개의 비동기 작업을 순차적으로 실행할 수 있도록 해준다.

 - then()메서드로 새로운 Promise객체를 반환해 다음 then()에서 이를 받아 또 다른 비동기 작업을 실행할 수 있도록한다.

promise
  .then((result) => {
    console.log(result);
    return addNumber(result);
  })
  .then((result) => {
    console.log(result);
    return addNumber(null);
  })
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.log(error);
  });

 

Promise

function addNumber(num) {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      if (typeof num === "number") {
        resolve(num + 10);
      } else {
        reject("num은 숫자가 아닙니다.");
      }
    }, 3000);
  });
  return promise;
}

const p = addNumber(0);
p.then((result) => {
  console.log(result);
  return addNumber(result);
})
  .then((result) => {
    console.log(result);
    return addNumber(null);
  })
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.log(error);
  });

addNumber(num)

 - promise객체를 생성해 반환한다. Promise는 3초뒤 resolve(), reject()반환하는 비동기 작업이다.

 - resolve() 는 num 숫자이면 호출되어 num + 10 결과를 반환한다.

 - reject()는 num이 숫자가 아닐때 호출되어 오류메시지를 반환한다.

 

체이닝의 장점

 - 가독성

 - 순차 처리

 - 오류 처리의 일관성

 

6. 비동기 - async, await

 - async, await를 사용해 비동기 코드를 동기 코드 처럼 작성할 수 있고 Promise체이닝보다 더 간결하고 직관적이게 코드를 작성할 수 있다.

 

6-1. async

 - 함수를 비동기 함수로 만들어주는 키워드로 함수가 Promise를 반환하도록 변환해주는 키워드로 함수 앞에 async키워드를 붙여 자동으로 Promise를 반환하게 한다. 

async function getDate() {
  return {
    name: "AR",
    id: "arangs",
  };
}
console.log(getDate());

getDate() 함수는 async()로 정의되어 있어 자동으로 Promise를 반환하고 이 결과는 then()으로 처리가 가능한다.

 

6-2. await

 - Promise가 처리될 때까지 기다리는 역할로 await는 async함수 내부에서만 사용이 가능하다.

 - await키워드는 Promise가 resolve될 때까지 기다린후 그 결과 값을 반환해 비동기 작업을 수행하고 await키워드를 사용해 동키 코드처럼 결과를 기다렸다가 다음 코드를 실행할 수 있도록 해준다.

async function getData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({
        name: "AR",
        id: "arangs",
      });
    }, 1500);
  });
}

async function printData() {
  const data = await getData();
  console.log(data);
}

printData();

async함수는 항상 Promise를 반환하며 함수 내에서 return을 사용해 해당 값이 Promise.resolve()로 감싸져 반환된다.

async함수 내에서는 await키워드를 사용해 비동기 작업을 기다린 후 결과를 처리할 수 있다.

 

await키워드는 함수 내부에서만 사용할 수 있다. 또, await키워드는 비동기 작업이 완료될때 까지 기다렸다가 결과를 반환한다. await는 Promise객체 앞에서 사용되며 비동기 작업의 완료를 기다린후에 다음 코드를 실행할 수 있다.

 

 

 

728x90
반응형
LIST