기술면접 준비

1. javascript 질문과 답안 (2)

개발쉐발 2023. 1. 11. 13:43
728x90
반응형

1. async, await 사용 방법을 설명해주세요.

비동기 처리 방식

자바스크립트는 싱글 스레드 프로그래밍 언어이기 때문에 비동기 처리가 기반입니다.
비동기 처리는 그 결과가 언제 반환될 지 알 수 없기 때문에 동기식으로 처리하는 기법들이 사용되어야 하는데요.
대표적인 동기식 처리 방식에는 setTimeout이 있고, callback, promise가 있습니다.


async와 await 사용 이유

async와 await 방법은 기존의 처리 방식의 문제점들을 보완하면서도 사용법이 훨씬 간단합니다.

 

Promise 코드

function p() {
return new Promise((resolve, reject) => {
	resolve('hello');
// or reject(new Error('error');
	});
}

p().then((n) => console.log(n));

async 코드

async function p2(){ // async을 지정해주면 Promise를 리턴하는 함수로 만들어줍니다.
	return 'hello'; 
}

p2().then((n) => console.log(n));

이처럼 async를 사용하면 promise 코드를 훨씬 직관적으로 나타낼 수 있습니다.
함수에 async만 붙이면 자동으로 promise 객체로 인식되고, return값은 resolve()값과 동일합니다.

 

async와 await 사용법

function 앞에 async를 붙여줍니다.
promise 객체 앞에 await를 붙여줍니다.

async가 붙은 함수는 promise 객체를 반환합니다. 따라서 .then((a) => {}를 이용할 수 있습니다.

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

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

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

function getFruites() {
    getApple()
        .then((a) => { // 리턴값이 promise의 resolve()이므로 then 가능
            getBanana()
                .then((b) => console.log(`${a} and ${b}`));
        })
}

getFruites(); // 결과 : apple and banana

await는 promise가 완료될 때까지 기다립니다.

await는 promise가 resolve한 값을 내놓습니다.

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

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

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

async function getFruites() {
    let a = await getApple(); // 리턴값이 promise의 resolve()이므로 대입 가능
    let b = await getBanana(); // getApple()이 처리되고 getBanana()가 처리됩니다.
    console.log(`${a} and ${b}`);
}

getFruites(); // 결과 : apple and banana

async의 예외처리 방법

promise 객체를 사용하기 때문에 .then / .catch를 이용해서 예외처리를 하는 방법이 있습니다.

async function p2() {
    throw 'error';
    //throw new Error("error");
    //await Promise.reject(new Error("error"));
    //return Promise.reject(new Error("error"));
}

p2()
    .then((n) => console.log(n))
    .catch((n) => console.log(n));

async는 throw로 예외처리 할 수 있습니다.

async function myAsyncErrorFun() {
  throw 'myAsyncError';
}

const resultAsync = myAsyncErrorFun().catch((e) => {
  console.error(e);
});

async 함수 내에서 await하는 Promise의 예외가 발생하면 throw를 반환합니다.

async function myAsyncFunCheckErrorNextCode() {
  console.log(new Date());
  await rejectedWait(1); // throw를 반환하고, 아래 구문들은 실행되지 않는다.
  console.log(new Date());
}
const resultAsyncFunCheckErrorNextCode = myAsyncFunCheckErrorNextCode();

async 함수 내에서 try / catch를 사용하는 방법이 있습니다.
(반드시 await를 통해서 동기식으로 만들어주어야 합니다)

async function myAsyncFunTryCatch1() {
  console.log(new Date());
  try {
    await rejectedWait(1); // throw 되었다.
  } catch (e) {
    console.error('myAsyncFunTryCatch1', e);
  }
  // 그 다음일들을 쭉쭉할 수 있다.
  console.log(new Date());
}
const resultAsyncFunTryCatch1 = myAsyncFunTryCatch1();

 

💡참고

 

Promise의 예외처리

reject를 이용하면 promise에 대한 예외처리를 진행할 수 있습니다.

function myPromiseErrorFun() {
  return new Promise((_, reject) => {
    reject('myPromiseError!');
  });
}
const resultPromise = myPromiseErrorFun().catch((e) => {
  console.error(e);
});

reject에 에러 메시지를 담을 수 있고, .catch(e) => { console.error(e) }를 통해 그 메시지를 표시할 수 있습니다.

 

2. Promise를 사용한 비동기 통신과 async, await를 사용한 비동기 통신의 차이를 설명해주세요.

 

1. js에서 비동기 처리는 왜 필요할까?

js는 동기적인 언어이지만, 서버에 데이터를 요청하는 등 대기시간 긴 작업의 경우 비동기 작업을 한다.

하지만 이 때문에 발생하는 문제가 있는데, 아래 예시를 살펴보자

getAnimals()는 서버에서 동물의 데이터를 받아오는 함수이다.

그리고 우리는 getAnimals()의 응답값을 받아 출력하고 싶다.

아래와 같이 작성하면 animals()의 값이 출력될까? 답은 아니다.

function printAnimals() {
  const animals = getAnimals();
  console.log(animals);
}

 

js는 동기적인 언어이지만, 대기시간 긴 작업의 경우 비동기로 작업 한다.

그렇기 때문에 getAnimals()보다 콘솔 출력이 먼저 발생하고, undefined가 출력되는 것이다.

function printAnimals() {
  const animals = getAnimals(); // 대기시간이 기네?
  console.log(animals);         // 우선 실행합니다~
}

printAnimals();  // undefined

 

그럼 만약 우리가 getAnimals()의 응답값(response)를 사용하고 싶다면 어떻게 해야할까?

비동기 처리방식인 promise, async await를 사용하는 것이다.

잠시만요! promise와 async await 둘 다 비동기 처리를 위해 사용되는데, 똑같은 거 아닌가?

용도는 똑같지만 다른 점이 분명 존재한다.

그럼 promise,async await에는 어떤 차이점 있는지 알아보자!

(1) async는 간결하다

앞선 예시를 다시 사용해보자.

우리는 getAnimals()를 호출하여 응답값을 받고, 이 응답값을 출력하고 싶다.

promise의 then방식이라면 아래와 같이 작성할 것이다.

function printAnimals() {
  getAnimals().then((data) => {
    console.log(data);
  })
}

 

그럼 async await로 어떻게 표현할까?

함수에 async 키워드를 적고, 비동기 대상에 await를 추가해주면 된다.

비동기 대상 함수에 await를 추가하면, '이 함수의 처리를 기다려!' 라는 의미가 되기에

await 함수 아래에 해당 함수의 응답값을 사용하는 구문을 추가해주면 된다.

async function printAnimals() {
  const animals = await getAnimals();  // 이 함수처리 끝날때까지 기다려!
  console.log(animals)                 // 응답값이 출력됨!
}

어? 그런데 간결성 측면에서 둘 다 큰 차이가 없는 거 같은데요?

짧은 예시로만 봤을 때 큰 차이를 못 느낄 것이다.

그렇다면 데이터를 요청하여 처리까지 한다면? promise의 then을 사용하면 아래와 같다.

function printAnimals() {
  return getAnimals()
    .then(data => {
      if (data.property) {
        return sampleFunc1(data)
          .then(anotherData => {
            console.log(anotherData)
          })
      }else {
        console.log(data)
      }
    })
}

then방식을 보면 라인 수가 많은 것은 물론 들여쓰기도 많아 복잡한 느낌이 든다.

그럼 async await를 쓰면 어떨까?

async function printAnimals() {
  const animals = await getAnimals();
  if (animals.property) {
    const sampleData = await sampleFunc1(animals);
    console.log(sampleData);
  }else {
    console.log(animals);
  }
}

then에 비해 많은 들여쓰기는 물론 라인도 차지 않는다.

또한 응답값을 명시적인 변수에 담아 사용하므로 직관적으로 변수를 인식할 수 있다.

이처럼 async await는 then 방식에 비해 간결하다는 장점을 가지고 있다.

 

(2) async는 에러 핸들링에 유리하다

 

서버에 데이터를 요청하는 작업을 하다보면, 에러가 발생할 수 있다.

이 때문에 우리는 에러 핸들링도 해주어야 한다.

이번에는 printAnimals()에서 에러가 발생한 게 아니라, JSON.parse에서 에러가 발생했다고 가정하자.

이 경우, then을 사용하면 내부에 추가적인 catch문을 적어줘야한다.

function printAnimals() {
  try {
      getAnimals()
      .then((response) => {
      const data = JSON.parse(response); // 여기서 에러 발생한다고 가정
      console.log(data);
    })
    .catch((err)=> {   // 추가적인 에러
      console.log(err)
    })
  }
  catch(err) {
    console.log(err)
  }
}

이 방식은 직관적이지 않을 뿐더러 에러를 처리하는 catch문이 중복된다.

 

하지만 async await를 사용하게 되면 하나의 catch만 해주면된다!

해당 catch문에서는 try 내부에서 발생하는 모든 에러를 접근할 수 있다.

async function printAnimals() {
  try {
      const data = await JSON.parse((getAnimals())
    console.log(data);
  }
  catch(err) {
    console.log(err)
  }
}

 

(3) async는 에러 위치를 찾기 쉽다

만약 프로미스를 연속으로 호출한다고 해보자.

이때 만약 어느 지점에서 에러가 발생하면 어떤 then에서 에러가 발생했는지 찾기가 어렵다.

function sample() {
  return sampleFunc()
    .then(data => return data)
    .then(data2 => return data2)
    .then(data3 => return data3)
    .catch(err => console.log(err))  // 결과적으로 문제가 발생했다
}

 

하지만 async를 사용하게 되면, 어떤 지점에서 에러가 발생했는지 쉽게 찾을 수 있다.

async function sample() {
  const data1 = await sampleFunc();      // 문제 발생시 data1값이 유효치 않음
  const data2 = await sampleFunc2(data1);
  return data2;
}



이처럼 promise의 then, async await는 용도는 같지만, 간결성, 에러 핸들링, 에러 위치 확인 측면에서 차이가 있음을 알게 되었다. 이외에도 async await은 디버그를 할 때 then과 달리 정확한 위치를 지정할 수 있는 장점이 있다.

 

 

참조 블로그 : 

https://mong-blog.tistory.com/entry/promise%EC%99%80-async-await%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90

 

promise와 async await의 차이점

이번 포스팅에서는 promise, async await의 차이점에 대해 알아보았다. 혹시 promise, async await을 모른다면 이 포스팅을 참고해보자! 1. js에서 비동기 처리는 왜 필요할까? js는 동기적인 언어이지만, 서

mong-blog.tistory.com

https://velog.io/@khyup0629/javascript-async%EC%99%80-await%EC%9D%98-%EA%B0%9C%EB%85%90%EA%B3%BC-%EC%82%AC%EC%9A%A9%EB%B2%95

 

[javascript] async와 await의 개념과 사용법

자바스크립트는 싱글 스레드 프로그래밍 언어이기 때문에 비동기 처리가 기반입니다. 비동기 처리는 그 결과가 언제 반환될 지 알 수 없기 때문에 동기식으로 처리하는 기법들이 사용되어야 하

velog.io

 

728x90
반응형