2 분 소요

자바스크립트 비동기 함수 관련 정리


세줄 요약.

  1. async function, await, Promise, .then() 얘네들은 비동기 처리를 위한 키워드임.
  2. async function, .then() 은 리턴값을 Promise 로 포장해서 준다.
  3. 포장된 값은 await 로 받을 수 있다.

예제

HTTP request

HTTP 요청하고 응답을 받기까지 시간이 걸리고 그 동안 다른 JS 코드가 돌아갈 수 있다.

1
2
3
4
5
let xhr = new XMLHttpRequest();
xhr.open("get", "url");
xhr.send();
let result = xhr.response;
console.log(result);

위 코드를 실행하면 undefined 가 나옵니다. 하지만 디버그 모드에서 한줄씩 천천히 실행하면 result 값이 나오게 됩니다. response 를 받아오는데 시간이 걸려서, JS 가 먼저 실행되는 경우 result 에 아무것도 할당되지 않기 때문입니다. 그래서 보통 아래처럼 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
let xhr = new XMLHttpRequest();
xhr.open("get", "url");
xhr.onload = function() {
  if(xhr.readyState === 4 && xhr.ststus === 200){
    let result = xhr.response;
    console.log(result);
  } else {
    console.log("failed to request");
  }
}
xhr.send();

이러면 요청 응답이 끝난 후 .onload() 가 실행되면서 비동기 처리를 할 수 있습니다. 하지만 코드가 위부터 아래로 순차적으로 실행되지 않고 나중에 실행될 코드를 중간에 작성해야 해서 맘에 안듭니다.

요청 및 응답을 받는 과정을 묶어서 async function 으로 만들어봅니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
async function sendGetRequest(url){
  let xhr = new XMLHttpRequest();
  xhr.open("get", url);
  xhr.onload = function() {
    if(xhr.readyState === 4 && xhr.ststus === 200){
      return xhr.response;
    } else {
      throw "error";
    }
  }
}
let result = await sendGetRequest("url");
console.log(result);

async function 은 리턴값을 Promise 로 감싸주기 때문에, await 로 (기다리고) 포장을 풀면 됩니다. 아래 코드와 동등한데…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function sendGetRequest(url){
  return new Promise(
    (resolve, reject) => {

      let xhr = new XMLHttpRequest();
      xhr.open("get", url);
      xhr.onload = function() {
        if(xhr.readyState === 4 && xhr.ststus === 200){
          resolve(xhr.response);
        } else {
          reject("error");
        }
      }

    }
  );
}

let result = await sendGetRequest("url");
console.log(result);

Promise 를 리턴하는 함수는 .then() 으로 다른 작업을 순서대로 실행하게 만들 수 있다. 이 때 .then() 내부에선 Promise 포장이 벗겨진 것 처럼 사용할 수 있지만, 리턴값은 또 다른 Promise 를 줍니다.

1
2
3
4
sendGetRequest().then(
  (resolve)=> {...},
  (reject) => {...}
);

.then()Promise 를 리턴하기 때문에, .then(...).then.(...)... 처럼 계속 이어갈 수 있어요.

1
2
3
4
sendGetRequest()
.then( handleResolve1, handleError )
.then( handleResolve2, handleError )
...

fetch()

Promise 를 쓰지 않고 XMLHttpRequest.onload() 를 써도 되지만, Promise 를 사용하여 좀 더 보기 좋게 코드를 짤 수 있습니다. 서버의 설정 파일 리소스를 읽을 때 이런 식으로 짤 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function loadConfig(fileName) {
  return (await fetch(fileName)).json();
}

loadConfig("myConfig.json").then(
  (resolve) => {
    let configInMemory = resolve;
  }
).then(
  (resolve) => {
    init();
    ...
    }
);

XMLHttpRequest.onload() 를 사용할 때와 달리 코드가 위에서 아래로 간단하게 읽힙니다.

마치며

저에겐 좀 생소한 문법이긴 한데, Promise 로 포장된다는 점만 잘 숙지하면 될 것 같습니다. 잘 이해하기 위해선 비동기 처리를 콜백으로 해결했을 때와 비교하면 되는데, 콜백 함수만 사용하면 실행 순서만큼 블록 레벨이 깊어지지만, Promise 를 쓰면 .then() 으로 연쇄 처리가 가능합니다.

태그:

카테고리:

업데이트: