[Javascript] async, await의 사용법


Study/Javascript, Jquery, CSS  2020. 3. 28. 09:00

안녕하세요. 명월입니다.


이 글은 Javascript의 async, await의 사용법에 대한 글입니다.


제가 이전에 프로미스(Promise)에 대해 설명한 적이 있습니다.

링크 - [Javascript] 프로미스(Promise) 사용법


이 프로미스를 사용하는 이유는 Javascript의 single thread로 사용되는 것을 timer를 이용해서 비동기적 계산하기 위한 오브젝트입니다.

<!doctype html>
<html>
 <head>
  <title>Document</title>
 </head>
 <body>
  <textarea id="console" style="width:500px;height:300px;"></textarea>
  <script>
    // 메시지에 날짜 추가 함수
    function addDate(msg) {
      // 날짜 생성
      let date = new Date();
      // 오늘 날짜
      return date.getFullYear() + "/" +(date.getMonth() + 1) + "/" + date.getDate() + "    " + msg;
    }
    // 개행 추가 함수
    function newLine(msg) {
      // 개행 추가
      return msg += "\r\n";
    }
    // 콘솔에 메시지 작성 함수
    function write(msg) {
      // 메시지 출력
      document.getElementById("console").value += msg;
      return msg;
    }
    // 0부터 9까지
    for(let i=0;i<10;i++) {
      // 날짜 추가
      msg = addDate(i);
      // 개행 추가
      msg = newLine(msg);
      // textarea 출력
      msg = write(msg);
      // 콘솔 출력
      console.log(msg);
    }
    // textarea에 출력
    write("end\r\n");
  </script>
 </body>
</html>

보시면 end라는 값이 textarea에 가장 하단에 출력됩니다. 뭐 당연한 소리입니다.

이걸 Promise를 통해 비동기 처리식으로 바꾸면 아래와 같습니다.

<!doctype html>
<html>
 <head>
  <title>Document</title>
 </head>
 <body>
  <textarea id="console" style="width:500px;height:300px;"></textarea>
  <script>
    // 메시지에 날짜 추가 함수
    function addDate(msg) {
      // 날짜 생성
      let date = new Date();
      // 오늘 날짜
      return date.getFullYear() + "/" +(date.getMonth() + 1) + "/" + date.getDate() + "    " + msg;
    }
    // 개행 추가 함수
    function newLine(msg) {
      // 개행 추가
      return msg += "\r\n";
    }
    // 콘솔에 메시지 작성 함수
    function write(msg) {
      // 메시지 출력
      document.getElementById("console").value += msg;
      return msg;
    }
    // 프로미스 생성
    function getPromise(i) {
      return new Promise((resolve, reject)=>{
        // setTimeout을 통해 이벤트 큐에 추가 (비동기식 처리)
        setTimeout(()=>{
          resolve(i);
        });
      });
    }
    // 0부터 9까지
    for(let i=0;i<10;i++) {
      // 프로미스를 통한 메소드 체인 패턴
      getPromise(i).then(addDate)
                   .then(newLine)
                   .then(write)
                   .then(x => {console.log(x)});
    }
    // textarea에 출력
    write("end\r\n");
  </script>
 </body>
</html>

이번에는 end라는 값이 textarea에 먼저 출력이 되었습니다.

이는 single thread가 먼저 처리가 되고 메시지 큐의 순서대로 처리되기 때문에 for문의 값이 후에 출력이 되는 것입니다.

Promise 설명을 정리하면 여기까지 입니다.


그런데, 개발자들이 이 Promise + setTimeout 조차 사용하기 귀찮았는지 이걸을 생략하는 키워드가 async입니다.

<!doctype html>
<html>
 <head>
  <title>Document</title>
 </head>
 <body>
  <textarea id="console" style="width:500px;height:300px;"></textarea>
  <script>
    // 메시지에 날짜 추가 함수
    function addDate(msg) {
      // 날짜 생성
      let date = new Date();
      // 오늘 날짜
      return date.getFullYear() + "/" +(date.getMonth() + 1) + "/" + date.getDate() + "    " + msg;
    }
    // 개행 추가 함수
    function newLine(msg) {
      // 개행 추가
      return msg += "\r\n";
    }
    // 콘솔에 메시지 작성 함수
    function write(msg) {
      // 메시지 출력
      document.getElementById("console").value += msg;
      return msg;
    }
    // 위 소스의 getPromise의 new Promise와 setTimeout을 생략하고 바로 async 키워드 하나로 비동기가 됩니다.
    async function getPromise(i) {
      return i;
    }
    // 0부터 9까지
    for(let i=0;i<10;i++) {
      getPromise(i).then(addDate)
                   .then(newLine)
                   .then(write)
                   .then(x => {console.log(x)});
    }
    write("end\r\n");
  </script>
 </body>
</html>

보시면 똑같은 결과가 나옵니다.

즉, async는 new Promise와 setTimeout을 합친 것의 키워드라고 할 수 있습니다.


그런데 이 프로미스는 main 환경에서 비동기로 값을 던져버리니 Promise의 안의 값을 취득할 수 없다는 문제점이 있습니다.

문제점은 아니고 사실 당연한 소리입니다. main 환경에서 실행되는 기준에서는 안의 setTimeout이 실행되기 전이니 값이 없습니다.

<!doctype html>
<html>
 <head>
  <title>Document</title>
 </head>
 <body>
  <textarea id="console" style="width:500px;height:300px;"></textarea>
  <script>
    // 메시지에 날짜 추가 함수
    function addDate(msg) {
      // 날짜 생성
      let date = new Date();
      // 오늘 날짜
      return date.getFullYear() + "/" +(date.getMonth() + 1) + "/" + date.getDate() + "    " + msg;
    }
    // 개행 추가 함수
    function newLine(msg) {
      // 개행 추가
      return msg += "\r\n";
    }
    // 콘솔에 메시지 작성 함수
    function write(msg) {
      // 메시지 출력
      document.getElementById("console").value += msg;
      return msg;
    }
    // 위 소스의 getPromise의 new Promise와 setTimeout을 생략하고 바로 async 키워드 하나로 비동기가 됩니다.
    async function getPromise(i) {
      return i;
    }
    // 0부터 9까지
    for(let i=0;i<10;i++) {
      // 프로미스의 최종 값을 받고 싶은데..
      var p = getPromise(i).then(addDate)
                           .then(newLine)
                           .then(write);
      // 실행되기 전이라 Promise 객체 밖에 없다.
      console.log(p);
    }
  </script>
 </body>
</html>

그래서 이 비동기 함수들을 실행하고 결과를 사용하기 위해서는 await키워드가 필요합니다.

<!doctype html>
<html>
 <head>
  <title>Document</title>
 </head>
 <body>
  <textarea id="console" style="width:500px;height:300px;"></textarea>
  <script>
    // 메시지에 날짜 추가 함수
    function addDate(msg) {
      // 날짜 생성
      let date = new Date();
      // 오늘 날짜
      return date.getFullYear() + "/" +(date.getMonth() + 1) + "/" + date.getDate() + "    " + msg;
    }
    // 개행 추가 함수
    function newLine(msg) {
      // 개행 추가
      return msg += "\r\n";
    }
    // 콘솔에 메시지 작성 함수
    function write(msg) {
      // 메시지 출력
      document.getElementById("console").value += msg;
      return msg;
    }
    // 위 소스의 getPromise의 new Promise와 setTimeout을 생략하고 바로 async 키워드 하나로 비동기가 됩니다.
    async function getPromise(i) {
      return i;
    }
    // main 환경에서는 Promise의 값을 어차피 받을 수 없으니 async 환경으로 만듭니다.
    async function run() {
      // 0부터 9까지
      for(let i=0;i<10;i++) {
        // await 함수를 사용하면 Promise의 결과를 전부 실행하고 결과를 리턴하게 된다.
        var p = await getPromise(i).then(addDate)
                                   .then(newLine)
                                   .then(write);
        // 결과가 콘솔에 출력
        console.log(p);
      }
    }
    // run 함수 실행.
    run();
  </script>
 </body>
</html>

저도 async까지는 자주 사용하는 편이었는데, await은 무엇일까 하는 생각이 많았습니다.

왜냐하면 await을 사용하지 않고 그냥 함수 하나 더 만들어서 거기서 출력하면 어차피 같은 결과가 나왔기에 크게 신경 쓰지도 않았었습니다.

이 글을 작성하면서 정리해보니 깔끔하게 이해가 되네요.


여기까지 Javascript의 async, await의 사용법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.