[Javascript] 비동기 구현 방식 - async await

짱쫑·2021년 12월 12일
0
post-thumbnail

Async & Await

async와 await은 promise를 좀 더 깔끔하게 사용할 수 있는 방법이다.
그렇다고 무조건 promise대신 async와 await을 써야하는건 아니고, 상황에 맞게 사용할 수 있다.

::Async

//사용자의 데이터를 백엔드에서 받아오는 함수
function fetchUser(){
	데이터를 가져오는데 10초가 걸리는 코드가 있다고 가정하자...
   
   return 'zzang';
}

const user = fetchUser();
console.log(user);

이렇게 비동기적 처리를 하지 않으면 10초가 지나 데이터를 가져올 때까지 아무거토 할수엄따.
만약 이 코드 다음으로 UI를 뿌리는 코드가 있다고 하면 그동안 아무런 화면이 출력되지 않는 현상을 만나볼 수 있다.

//promise대신 async
async function fetchUser(){
	데이터를 가져오는데 10초가 걸리는 코드가 있다고 가정하자...
    
    return 'zzang';
}

const user = fetchUser();
console.log(user);

프로미스를 쓰지 않고 함수 앞에 async를 쓰게 되면 자동적으로 함수안의 코드블럭들이 promise로 변환이 되어 진다.

::Await

await은 async가 붙은 함수 안에서만 사용할 수 있다.

//프로미스를 리턴하는 delay함수가 있다. 
function delay(ms){
  //정해진 ms가 지나야 resolve를 호출하는 프로미스를 리턴한다
  return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple(){
  //1초가 지나면 리졸브를 호출하는 프로미스를 전달받아 사과를 리턴
  //await을 쓰면 뒤의 1초가 지날때까지 기다려 준다. 
  await delay(1000); 
  return '🍎';
}
async function getBanana(){
  await delay(1000);
  return '🍌';
}

위의 바나나 함수를 프로미스의 chaining코드로 바꿔보면

function getBanana(){
  return delay(1000)
    .then (() => '🍌');
}

이렇게 되는데 chainin을 하는 것보다 await을 쓰는것이 더욱 효율적이다. (개인차있음)

//여기서 사과와 바나나를 다 받는 함수를 만들어보자
function pickFruits(){
  return getApple()()
  .then(apple => {
    return getBanana()
    .then(banana => `${apple} + ${banana}`)
  })
}

//사과와 바나나를 다 받아오면
pickFruits().then(console.log);

//이렇게 또 콜백지옥이 만들어졌는데 이를 개선해보자

async function pickFruits(){
  const apple = await getApple();
  const banana = await getBanana();
  return `${apple} + ${banana}`;
}

pickFruits().then(console.log);

async를 이용해서 콜백지옥을 벗어날 수 있었지만 아직 에러처리를 할 수는 없다.

async function getApple(){
  await delay(1000); 
  throw 'error';
  return '🍎';
}
async function getBanana(){
  await delay(1000);
  return '🍌';
}

//try, catch를 통해 에러를 핸들링해보자
async function pickFruits(){
  try{
    const apple = await getApple();
    const banana = await getBanana();
  } catch(){
  
  }
  return `${apple} + ${banana}`;
}
//이렇게 만들면 우리가 기존의 쓰던 try, catch문과 비슷해 알아보기는 쉽다. 

pickFruits().then(console.log);

그러나 위의 코드를 보면 await때문에 하나하나 받아오는데 또 delay만큼의 시간이 걸린다. (2초)
이제 await를 병렬처리 시켜보도록 하자

async function getApple(){
  await delay(1000); 
  throw 'error';
  return '🍎';
}
async function getBanana(){
  await delay(1000);
  return '🍌';
}

//try, catch를 통해 에러를 핸들링해보자
async function pickFruits(){
  try{
    const apple = await getApple();
    const banana = await getBanana();
  } catch {
  
  }
이런식으로 기존에 쓰던 try, catch를 이용해서 에러처리를 할 수 있다.
try와 catch를 같이 쓸 수도 있고 catch만 사용할 수 있다(2가지).

async function pickFruits(){
  //사과와 바나나를 받아오는데에는 서로 연관이 되어 있지 않아 사과에서 1초를 기다리고 바나나에서 1초를 기다리는 것은 비효율 적이다. 
  const apple = await getApple();
  const banana = await getBanana();
  return `${apple} + ${banana}`;
}

//그래서 기다릴 필요없도록 개선해보자
async function pickFruits(){
  //프로미스를 만드는 순간 코드블럭은 바로 실행된다고 했었다. 
  const applePromise = getApple();
  const bananaPromise = getBanana();
  const apple = await applePromise;
  const banana = await bananaPromise;
  
  //이렇게 하면 각 1초씩이 아니라 1초만에 사과와 바나나를 동시에 가져온다.
  return `${apple} + ${banana}`;
}

::Promise API
앞서 말한대로 사과와 바나나는 연관이 없다. 그래서 바로 위처럼 코드를 더럽게 쓸 필요가 없다.
promise API를 이용하면 더 간단하게 만들 수 있다.

function pickAllFruits(){
  //all을 이용하면 프로미스의 배열을 전달하면 모든 프로미스들이 병렬적으로 다 받을때까지 모아준다.
  //겟애플과 겟바나나를 배열로 전달하게 되면
  return Promise.all([getApple(), getBanana()])
  //다 받아지면 그러면(then) 다 받아진 배열이 다시 전달된다. 그 배열을 join을 이용해 string으로 묶어준다. 
  .then(fruits => fruits.join(' + '));
}
pickAllFruits().then(console.log); //🍎 + 🍌

그럼 다른 예시로 어떤 것이든 먼저 가져올 수 있는것을 받아오고 싶다 라는 코드를 작성해보자
(사과가 2초걸리고 바나나가 1초걸린다)

v=async function getApple(){
  await delay(2000); 
  throw 'error';
  return '🍎';
}
async function getBanana(){
  await delay(1000);
  return '🍌';
}

function pickOnlyOne() {
  //race를 이용하게 되면 배열에 전달된 프로미스 중에서 가장먼저 값을 리턴하는 함수만 전달된다.
  return Promise.race([getApple(), getBanana()])
}

pickOnlyOne().then(console.log); // 🍌
profile
不怕慢, 只怕站

0개의 댓글