본문 바로가기

Language/Javascript

[Javascript] async와 await에 대하여

async와 await를 이용하면 Promise를 좀 더 편하게 사용할 수 있다.

즉 읽고, 쓰기 쉬운 비동기 코드 작성이 가능하다!

 

async 함수

async 키워드는 function 앞에 위치한다.

async function func() {
   return true;
}
  • function 앞에 async를 붙이면 해당 함수는 항상 Promise를 return함.
  • Promise가 아닌 값을 반환하더라도 resolved promise로 값을 감싸 이행된 Promise가 반환되도록 함.
async function func(){
   return Promise.resolve(1);
}

func().then(alert)
//1로 alert 표시.
async function func(){
   return 1;
}

func().then(alert);
//1로 alert 표시.

위 두 예시는 명시적으로 Promise를 반환할 경우, 아닐 경우이다.

 

await

await는 아래와 같이 사용할 수 있음.

// await는 async 함수 안에서만 동작한다.
let value = await promise;
  • await 키워드는 promise가 처리될 때까지 기다린다는 뜻이다.
  • await는 반드시 async 함수 안에서만 동작한다.
async function func() {
   let promise = new Promise((resolve, reject)=>{
      setTimeout(()=> resolve("완료!"), 1000)
   });
   
   let result = await promise; // Promise가 이행될 떄까지 기다림.
   
   alert(result); //"완료!" alert 출력
}
  • executor를 호출하고 await에서 잠시 중단되었다가 Promise가 처리되면 실행이 재개.
  • 이때 Promise의 resolve 파라미터로 전달되는 결과값이 result 변수에 할당된다.
  • 따라서 1초 뒤에 alert창으로 "완료!"라고 출력된다.
  • await과정에서 Promise가 처리되는 동안에는 엔진이 다른 일(다른 Task)을 할 수 있기 때문에 CPU리소스 낭비가 되지 않는다.(참고)

Tips!

await는 최상위 레벨 코드에서 작동하지 않음!

// 최상위 레벨 코드에선 문법에러 발생
let response = await fetch('/emp/sch.json');
let user = await response.json();

해결책 : 익명 async 함수로 코드를 감싸면 된다.

(async ()=>{
   let response = await fetch('/emp/sch.json');
   let user = await response.json();
   ...
})();

await는 'thenable' 객체를 받는다.

  • promse.then 처럼 await에도 thenable 객체(then 메서드가 있는 호출 가능한 객체)를 사용할 수 있음.
class Thenable {
   constructor(num) {
      this.num = num;
   }
   then(resolve, reject){
      alert(resolve);
      //1000밀리초 후에 이행됨
      setTimeout(()=>resolve(this.num * 2), 1000);
   }
};

async function func(){
   //1초 후, result에 2 대입.
   let result = await new Thenable(1);
   alert(result);
}

func();
  • await는 .then이 구현되어있으면서 Promise가 아닌 객체를 받으면, 내장 함수 resolve와 resolve를 인수로 제공하는 메서드인 .then을 호출.(Promise의 executor가 하는 일과 동일)

async 클래스 메서드

메서드 이름 앞에 async를 추가하면 async 클래스 메서드를 선언할 수 있음.

class Waiter {
   async wait(){
      return await Promise.resolve(1);
   }
}

new Waiter()
   .wait()
   .then(alert); //alert로 1출력.
  • async 메서드와 async함수는 Promise를 반환하고 await를 사용할 수 있다는 점에서 동일.

여러 개의 Promise가 모두 처리되길 기다려야 하는 상황

기다려야 하는 Promise들을 Promise.all로 감싸고 await처리를 해준다.

//Promise 처리 결과가 담긴 배열을 기다림.
let results = await Promise.all([
   fetch(url1),
   fetch(url2),
   ...
]);

 

에러 핸들링

Promise가 정상적으로 이행되면 await promise는 Promise 객체의 result에 저장된 값을 반환.

반면 Promise가 reject되면 throw문을 작성한 것 처럼 에러가 발생.

async function func(){
   await Promise.reject(new Error("에러 발생!");
}

위코드와 아래 코드는 동일.

async function func(){
   throw new Error("에러 발생!");
}

await가 던진 에러는 try..catch를 사용해 처리할 수 있다.

async function func(){
   try {
      let response = await fetch('http://wrong-url.com');
   } catch(err) {
      //에러를 catch하는 부분.
      alert(err);
   }
}

func();

try..catch문을 사용하지 않고 아래와 같이 error를 catch할 수도 있음.

async function func(){
   let response = await fetch('http://wrong-url.com');
}

func().catch(alert);