프로그래밍/JavaScript

자바스크립트 비동기를 제대로 이해해보자 | async, await

choar 2022. 9. 16. 12:15
반응형

자바스크립트 비동기를 제대로 이해해보자 | async, await

 

💡 다음 코드를 실행하면 콘솔에 출력이 어떤 순서대로 나올지 예측해보자.

const test = async () => {
  console.log("hello");
  const result2 = test2();
  const result3 = test3();
  console.log("world");
  await sleep(2500);
  console.log(result2);
  console.log(result3);
};

const test2 = async () => {
  console.log("a");
  await sleep(2000);
  console.log("b");
  return "test2";
};

const test3 = async () => {
  console.log("c");
  await sleep(3000);
  console.log("d");
  return "test3";
};

const sleep = (ms) => {
  return new Promise((resolve, _) => setTimeout(resolve, ms));
};

test();

 

위 코드의 실행 순서를 알아내려면 일단 async, await에 대해 이해해야 한다.

 

async 함수는 항상 Promise를 반환한다.

return 값이 있을 경우 result가 return 값인 Promise를 반환한다.

async function f() {
  return 1;
}

console.log(f()); // Promise { 1 }

await 키워드는 async 함수 내에서만 사용되며,

Promise가 이행될 때까지 기다린 후 결과를 반환한다.

async function f1() {
  return 1;
}
async function f2() {
  console.log(await f1());
}

f2(); // 1

 

자바스크립트에서 비동기 함수는 논블로킹 방식으로 작동한다.

동기는 응답을 기다리는 것, 비동기는 응답을 기다리지 않는 것이고,

블로킹은 현재 진행 중인 작업에서 다른 작업을 요청하면 응답에 대한 '완료'가 될 때까지 아무것도 할 수 없는 것,

논블로킹은 요청한 응답이 완료될 때까지 기다리지 않는 것이다.

 

맨 위 코드를 다시 보면 test 함수 내의 코드는 다음과 같은 순서로 처리될 것이다.

const test = async () => {
  console.log("hello"); // 1
  const result2 = test2(); // 2
  const result3 = test3(); // 3
  console.log("world"); // 4
  await sleep(2500); // 5
  console.log(result2); // 6 
  console.log(result3); // 7
};

const test2 = async () => {
  console.log("a");
  await sleep(2000);
  console.log("b");
  return "test2";
};

const test3 = async () => {
  console.log("c");
  await sleep(3000);
  console.log("d");
  return "test3";
};

const sleep = (ms) => {
  return new Promise((resolve, _) => setTimeout(resolve, ms));
};

test();

주의해야 할 부분은 test 함수 내 2번째, 3번째 문이다.

test2, test3은 비동기 함수기 때문에 2번째, 3번째 문이 ( Promise { <pending> } )으로 평가된 후,

test2, test3 내의 코드는 각각 병렬적으로 진행되게 된다. 

 

콘솔 출력 순서

출력 순서를 그림으로 살펴보면 위와 같다.

각각의 출력 간의 시간 차이를 ms 단위로 표시했다.

0의 경우 정확히 0ms라기보다는 앞의 출력이 끝나고 거의 바로 뒤 출력이 이루어짐을 의미한다.

 

분홍색 형광펜으로 표시한 부분은 test2 함수, 연두색 형광펜으로 표시한 부분은 test3 함수이다.

console.log(result2)의 경우 test2 함수는 return 문까지 끝난 시점이므로, Promise { 'test2' }가 출력된다.

console.log(result3)의 경우 test3 함수는 return 문까지 도달하지 못한 시점이므로, Promise { <pending> }이 출력된다.

 

 

💡 test3() 앞에만 await를 붙이면 실행 순서가 어떻게 달라질까?

const test = async () => {
  console.log("hello");
  const result2 = test2();
  const result3 = await test3(); // 수정한 부분 ⭐️
  console.log("world");
  await sleep(2500);
  console.log(result2);
  console.log(result3);
};

const test2 = async () => {
  console.log("a");
  await sleep(2000);
  console.log("b");
  return "test2";
};

const test3 = async () => {
  console.log("c");
  await sleep(3000);
  console.log("d");
  return "test3";
};

const sleep = (ms) => {
  return new Promise((resolve, _) => setTimeout(resolve, ms));
};

test();

async 함수 내에서 await를 만나면 await 뒤의 Promise가 이행될 때까지 기다린다.

따라서 test3 함수가 return을 끝낸 뒤에야 world가 출력된다.

test2의 경우 await 키워드가 붙지 않았기 때문에 test 함수 내 코드와 병렬적으로 실행되었다.

 

앞에서 서술했듯, await는 Promise가 이행된 값을 가져오므로

console.log(result3)의 경우 Promise { 'test3' } 가 아니라 문자열 'test3'를 출력한다.

따라서 출력 순서는 다음과 같다.

 

콘솔 출력 순서

🌟 내용에 오류가 있다면 댓글 달아주시면 감사하겠습니다.


References

『개발자가 되기 위해 꼭 알아야 하는 IT 용어』, 고승원 외

https://ko.javascript.info/async-await

반응형