FE/JavaScript

[JS] async / await

mandelina 2022. 5. 23. 23:38
콜백 지옥 탈출을 위해 Promise 가 나왔는데  async / await 는 뭐야?! 

 

 

 

 

Promise는 비동기 작업이 많아져도 콜백 함수처럼 코드의 깊이가 깊어지지 않는다.

하지만 다른 문제점이 있다.

 

1. 특정 조건에 따라 분기를 나누기 어렵다.

2. 어떤 부분에서 에러가 발생했는지 파악하기가 어렵다

3. 프로미스 체이닝도 반복되면 가독성이 떨어져 .then 지옥이 발생할 가능성이 있다.

 

→ 따라서 async/await가 등장하였다.

 

 


 

 

async/await

 

- 프로미스보다 가독성이 좋고 동기 프로그래밍을 작성하는 방식과 유사

- Promise 기반으로 동작

 

 

 

async선언

- async 키워드함수 앞에 붙여 사용한다.

 

async function 함수이름() {
    return 결과 값;
}

 

 

- async가 붙은 함수는 항상 반환값을   resolved로 하는 Promise를 반환한다.

- 반환값에 Promise가 명시되지 않더라도 암묵적으로 Promise로 감싸진다.

 

 

[예제-1]

async function snack() {
    return 1;
}

snack().then(alert); // '1' 반환

 

[예제-2]

function snack() {
    return Promise.resolve(1);
}

snack().then(alert); // '1' 반환

 

 

- 예제1에선 return값에 Promise가 명시되어있지 않지만 암묵적으로 Promise로 감싸져 예제2와 같은 의미를 갖는다.

 

 

 

 


 

async/await 사용

 

 

[예제-3]

async function 함수이름() {
		await 비동기 처리 메서드 이름();
}

 

[예제-4]

const 함수이름 = async () ⇒ {
		await 비동기 처리 메서드 이름();
}

 

예제3처럼 사용해도 되고 , 예제 4처럼 화살표 함수처럼 역시 사용이 가능하다.

 

 

 

 

▷  await 키워드 ?

프로미스가 settled 상태(비동기 처리가 수행된 상태)가 될 때 까지 대기하다가,

settled 상태가 되면 프로미스가 resolve한 결과를 반환

 

( settled 상태 : pending된 상태가 아닌 상태. 즉 resolved or rejected )

 

 

 

출저 : 알잘딱깔센 JS

 

 

 

  await 키워드 사용시 주의

- await 키워드는 반드시 async  함수 내부, Promise 앞에서만 사용해야한다.

 

 

 

 


 

async/await 제어 흐름

 

const snack = () => Promise.resolve('4') // 4번

async function mySnack() {
	console.log('3') // 3번
    const res = await snack() // 4번
    console.log(res) // 6번
}

console.log('1') // 1번
mySnack(); // 2번
console.log('5') // 5번

 

1. console.log('1') 이 stack에 적재되고 1 출력 후 빠져나옴

 

2. mySnack( ) 호출 되어 적재 후 그 안에 있는 console.log('3')을 적재하여 3 출력 

 

3. res = await snack() 으로 선언되어있음,  이때 await를 만나 queue로 들어가고 pending이 풀릴때 까지 기다린다. (즉 , call stack에 아무것도 없을때까지 기다림)

 

4. 기다리는동안 console.log('5') stack에 적재하고 5 출력 후 빠져나옴

 

5. 그러면 더이상 stack에 아무것도 없으니 console.log(res)를 통해 '4' 출력

 

 

 

[결과]

 

 

 


 

async/await 실용 예제

 

[ 예제-1 ] 

 

function cook(ms) {
		return new Promise(resolve => setTimeout(resolve, ms));
}

const myCake = async () => {
		await cook(1000);
		return '케이크';
};

const myCoffee = async () => {
	  await cook(500);
	  return '커피';
};
const myCookie = async () => {
	  await cook(5000);
	  return '쿠키';
};

async function asyncProcess() {
	  const cake = await myCake();
	  console.log(cake);
	  const coffee = await myCoffee();
	  console.log(coffee);
	  const cookie = await myCookie();
	  console.log(cookie);
}

asyncProcess();

 

실행 순서를 보면 , asyncProcess()이 호출되어 cake , coffee , cookie를 콘솔로 찍어낼것이다.

이때 await 키워드를 만나고, 각 my~함수로 가게 되면 cook(ms)이라는 함수를 또 만나게 된다.

이 cook(ms)는 Promise인 setTimeout를 리턴하고 있다.

따라서 이 코드는 순차적으로 1초후 케이크리턴 , 0.5초후 커피리턴 , 5초후 쿠키를 리턴 하여

총 6.5초가 소요된다.

 

 

[ 예제-2 ] 

 

 setTimeout(() => {
        console.log("5초끝");
      }, 5000);

function cook(ms) {
		return new Promise(resolve => setTimeout(resolve, ms));
}

const myCake = async () => {
	  await cook(1000);
	  return '케이크';
};

const myCoffee = async () => {
	  await cook(500);
	  return '커피';
};
const myCookie = async () => {
	  await cook(5000);
	  return '쿠키';
};

async function promiseProcess() {
	const results = await Promise.all([myCake(), myCoffee(), myCookie()]);
	console.log(results);
}

promiseProcess();

 

 

Promise.all을 사용하면 병렬실행을 하게된다.

즉 , 케이크1초 커피0.5초 쿠키5초를 병렬실행하면 마지막으로 리턴되는 쿠키를 기준으로 5초에 results가 출력된다. 

 

 


 

다음엔 fetch 에 대해 알아보겠다.