Async & Await

BaeSeong-min·2025년 1월 7일
0

ES6 문법공부

목록 보기
20/22
post-thumbnail

📁Async & Await

비동기 작업 간의 순서를 지정하기 위해서 프로미스 체이닝을 사용한다. asyncawait 키워드를 사용함으로써 복잡한 체이닝 없이도 비동기 작업 간의 순서를 지정할 수 있다. 그리고 코드가 한 줄씩 실행되기에, 비동기 작업을 처리하는 코드를 동기적으로 작성하여 읽기 쉽게 한다.

📂Async

🚀 함수 앞에다가 async 키워드를 붙이면 항상 Promise를 반환하는 비동기 함수가 된다.

🗂️Async로 정의된 비동기 함수가 값을 반환한 경우

async는 비동기 함수를 정의할 때 사용하는 키워드다. function 키워드 앞에 async를 붙여주면 항상 Promise 객체를 반환하는 비동기 함수가 된다.

async function getUser() {
  return '성민';
}

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

user.then((name) => console.log(name)); // '성민'

항상 Promise 객체를 반환하는 함수 안에서 어떤 값을 반환하면, 내부적으로Promise.resolve() 메서드로 값이 감싸서 fulfilled 상태이면서 Promise.resolve() 메서드의 인자가 결과값Promise 객체가 반환된다.

🗂️Async로 정의된 비동기 함수가 Promise를 반환한 경우

const promise = new Promise(resolve => resolve("개발자"));

async function getUser() {
  return promise;
}

const user = getUser();

user.then((name) => console.log(name)); // "개발자"

Promise 객체를 직접적으로 반환한다고 해서, Promise로 한번 더 감싸져서 반환되지 않는다. 직접적으로 Promise를 반환한다고 명시하면, 반환한다고 명시한 Promise 그대로 반환된다.

🗂️Async 사용 예제

function networkRequest() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('데이터를 받아왔습니다.');
      resolve();
    }, 2000);
  });
}

async function getUser() {
  networkRequest();
  return '성민';
}

const user = getUser();

user.then((name) => console.log(name)); // "성민" (2초 뒤) "데이터를 받아왔습니다."

networkRequest() 함수는 네트워크 요청에서 서버로부터 데이터를 받아오는 데 2초가 걸린다는 것을 흉내내는 함수이다. 위 코드를 실행하면 setTimeout() 비동기 함수를 사용하는 networkRequest()비동기적으로 수행되기에 끝날 때까지 기다리지 않고, 바로 다음 작업으로 넘어간다. 따라서 "성민"이 먼저 출력된 것이다.

비동기 작업 간의 순서를 지정해주고 싶으면 await를 사용하면 된다.


📝참조: 비동기 작업이 완료된 후에는 콜백함수나 Promise, async/await을 사용해서 결과를 처리하거나 후속 작업을 실행한다. 이때, 비동기 작업이란 작업이 시작된 후 결과가 준비될 때까지 다른 작업을 계속 수행할 수 있는 작업 방식이다.


📝참조: async 키워드가 붙지 않는 비동기 함수는 무조건 Promise를 반환해야 한다는 규칙은 없다. 다만 비동기 작업의 결과를 처리하거나, 이후 작업과 연결할 수 있게 하기 위해서는 Promise를 반환하는 것이 일반적이다.


📂Await

🗂️Await을 사용한 예제

function networkRequest() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('데이터를 받아왔습니다.');
      resolve('서버 1');
    }, 2000);
  });
}

async function getUser() {
  const result = await networkRequest();
  console.log(result);
  return '성민';
}

const user = getUser();

user.then((name) => console.log(name)); // (2초 뒤) "데이터를 받아왔습니다." "서버 1" "성민" 

awaitPromise가 완료될 때까지, 즉 fulfilled 될 때까지 기다려주는 역할을 한다. networkRequest()는 2초 뒤에 fulfilled 상태의 Promise 객체를 반환한다. 그리고, awaitPromise의 결과값을 반환한다. 따라서, 반환된 Promise의 결과값은 result 변수로 받았다.

await 키워드는 반드시 async 키워드가 붙은 함수 내부에서만 사용할 수 있다. 그렇지 않으면 에러가 발생한다.


📝참조: awaitPromise 객체가 아닌 값이라도 처리를 한다. 값일 때는, 내부적으로 Promise.resolve()로 해당 값을 감싼다. 이때, fulfilled 상태의 Promise가 만들어지기에 바로 다음 작업으로 넘어간다.

async function test() {
  const value = await 42; // 42는 Promise가 아님
  console.log(value); // 42
}

test();

🗂️Promise Chainning을 사용한 예제 (Await 사용한 코드와 비교)

function networkRequest() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('데이터를 받아왔습니다.');
      resolve('서버 1');
    }, 2000);
  });
}

function getUser() {
    return networkRequest()
      .then(() => {
        return networkRequest();
    })
      .then(() => {
        return "성민";
    });
}

const user = getUser();
user.then((name) => {
    console.log(name);
})

Promise Chainning을 사용한 코드보다 async & await를 사용한 코드가 동기적으로 작성되어 읽기 쉬운 모습이다.


📂Async & Await 사용 예제

function networkRequest() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve();
        }, 2000);
    });
}

async function getUser() {
    await networkRequest();
    return "성민";
}

async function getTodo() {
    await networkRequest();
    return ['청소하기', '밥먹기'];
}

async function getData() {
    const user = await getUser();
    const todo = await getTodo();
    console.log(`${user}${todo} 해야합니다.`);
}

getData();
// (4초 뒤) "성민님 청소하기,밥먹기 해야합니다."

마찬가지로, asyncawait를 사용하면 비동기 작업을 처리하는 코드를 동기적으로 작성하여 읽기 쉽게 할 수 있다.


📂try-catch문을 이용한 에러처리

function networkRequest() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve();
        }, 2000);
    });
}

async function getUser() { // getUser()에서 에러 발생했다고 가정.
    throw new Error("에러가 발생했어요!");
    return "성민";
}

async function getTodo() {
    await networkRequest();
    return ['청소하기', '밥먹기'];
}

async function getData() {
    let user;
    try {
        user = await getUser();
    } catch(error) {
        console.log(error.message);
        user = "익명" // getUser()에서 에러가 발생하는 경우, user를 '익명'으로 만든다.
    }

    const todo = await getTodo();
    console.log(`${user}${todo} 해야합니다.`);
}

getData();
// "에러가 발생했어요!" (2초 뒤) "익명님 청소하기,밥먹기 해야합니다."

async 함수 내부에서 오류가 발생하는 경우, try-catch문으로 예외처리를 해줄 수 있다.


📂Fetch API

async function fectchData() {
    const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
    const data = await response.json();
    console.log(data);
}

fectchData();
// { userId: 1, id: 1, title: 'delectus aut autem', completed: false }

fetch 비동기 함수특정 URL로 네트워크 요청을 하고 데이터를 받아오는 Web APIs이다. 네트워크 요청을 보낼 URL은 JSONPlaceholder에서 제공하는 test용 API를 사용한다. fetch() 함수 앞에 await을 붙여줘서, 서버로부터 데이터를 받아오는 일을 완료할 때까지 기다린다. 서버로부터 받아온 데이터는 추출할 수 없는 Response 객체이기 때문에, 데이터를 추출하기 위해선 특정 메서드를 사용해서 데이터 형변환을 시켜줘야한다. 그러기 위해서 Promise 객체를 반환하는 json() 메서드를 사용한다. json() 비동기 함수의 작업이 끝나면 반환하는 Promise 결과값을 data에 전달한다.

profile
성민의 개발 블로그 🔥

0개의 댓글