Front-end/깊게 파고들기

Javascript 비동기

아지송아지 2022. 4. 17. 00:30

안녕하세요.

오늘은 개발을 하면서 빼놓을 수 없는 비동기에 대해서 알아보겠습니다.

Event Loop에 대한 지식이 있다면 이해하기 더 쉬우실 겁니다.

 

 

 

동기? 비동기?


Javascript에서의 비동기를 알아보기 전에 동기와 비동기의 차이점을 아시나요?

동기

서버에 요청을 보냈을 때 응답이 돌아와야만 다음 동작을 실행합니다. 한 번에 한 가지씩 진행되는 것이죠.

 

비동기

특정 로직의 실행이 끝날 때까지 기다려주지 않고 나머지 코드를 먼저 실행하는 것입니다.

 

 

 

Javascript 비동기


자바스크립트에서 비동기가 필요한 이유는 서버로 데이터를 요청했을 때 모든 코드를 한줄한줄 기다렸다가 실행을 할 수 없기 때문입니다. 

 

자바스크립트에서 비동기를 처리하는 방법 세 가지를 소개해드리겠습니다.

 

1. Callback

콜백이란 다른 함수의 전달 인자로 받은 함수입니다.

setTimeout(() => {
    console.log("Hello");
    setTimeout(() => {
    	console.log("Callback")
        setTimeout(() => {
            console.log("Hell")
        }, 1000)
    }, 1000)
}, 1000)

만약 서버 통신 후 받은 값을 콜백 형태로 받고 그 속에서 다시 콜백 형태의 요청을 하고... 이런 식으로 반복되면 코드는 점점 콜백 함수들로 가득 차게 됩니다. 이게 바로 콜백 지옥입니다.

 

콜백 지옥은 여러 문제점들을 발생시킵니다.

 

* 코드의 가독성이 떨어집니다.

* 코드를 이해하기 힘들어집니다.

* 로직 변경, 수정, 추가, 에러 처리가 어렵습니다.

 

이렇게 되면 개발자들은 코드의 안정성과 확장성 그 무엇도 보장받을 수 없습니다.

 

2. Promise

콜백 지옥을 막기 위해 Promise라는 메서드가 나왔습니다.

new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(10);
  }, 1000);
})
.then(function(result) {
  console.log(result);
  return result + 10;
})
.then(function(result) {
  console.log(result); // 20
})

new Promise()를 통해 사용할 수 있으며 생성하고 종료될 때까지 "대기, 성공, 실패"의 상태를 갖습니다.

 

처음 new Promise()를 호출하면 대기 상태가 됩니다.

resolve()가 실행되면 성공 상태가 됩니다. 해당 상태가 되면 then()으로 이동하여 다음 로직을 처리합니다.

위 코드에는 나와있지 않지만 reject()가 실행되면 실패 상태가 되어 catch()로 이동합니다.

 

promise 또한 장단점이 있습니다.

장점

* 구조화된 콜백을 작성할 수 있습니다.

* then으로 인해 엄격한 순서로 콜백이 호출됩니다.

* 에러처리가 간결해집니다.

* 이전 콜백 지옥 방법보다는 가독성이 좋습니다.

 

단점

* 코드의 양이 길고 아직까지는 가독성이 좋지 않습니다.

* 정확하고 세심함 예외 처리가 어렵습니다.

* 여러개씩 전달되어야 하는 값들은 한계가 있습니다.

* 단순 콜백 처리와 비교했을 때 성능이 저하됩니다.

 

3. async, await

마지막 비동기 처리 방식입니다. 자바스크립트의 비동기 처리 패턴 중 가장 최근에 나온 문법입니다.

콜백 함수와 프로미스의 단점을 보완하고 가독성이 뛰어납니다.

 

코드로 먼저 설명드리겠습니다.

async function getName(){
    // fetchUserName은 서버에서 이름을 가져오는 fetch 함수라고 가정
    const name = await fetchUserName();
    console.log(name);
}

만약 이 코드에서 async와 await을 사용하지 않았다면 console.log(name);에는 이름이 출력되지 않습니다.

async와 await을 활용하면 fetch가 모두 끝날 때까지 기다렸다가 아래 코드들이 실행됩니다.

 

사용법은 간단합니다.

함수 앞에 async라는 예약어를 붙입니다. 그리고 프로미스 객체를 반환하는 비동기 메서드 앞에 await을 붙입니다.

일반 함수 앞에 await을 사용하면 작동하지 않습니다.

(fetch 함수는 Promise 객체를 반환합니다.)

에러 처리는 try/catch 문 혹은 api 에러에 대한 메시지를 반환받아 조건문으로 처리하는 방법 등이 있습니다.

 

 

asycn await은 어떻게 동작할까요?

관심 없으신 분들은 안 보셔도 괜찮습니다.

0. 위와 같은 코드가 있습니다.

 

1. "Before function"이 출력됩니다.

 

2. myFunc()이 실행되고 "In function"이 출력됩니다.

 

3. await을 만나면 myFunc을 Microtask Queue에 넣습니다.

 

4. "After function!"을 출력합니다.

 

5. 콜스택이 비어있으니 myFunc()가 다시 실행되며 res가 출력됩니다.

 

 

 

마치며


이번 글은 작성하면서 어디까지 설명을 드려야 하고 무엇을 뺄지 고민을 많이 했습니다.

최대한 간결하면서도 핵심적인 부분들만 전달하려고 했는데 그게 잘 됐는지 모르겠네요.

 

만약 궁금하시거나 제 글에서 수정해야 할 부분이 있다면 알려주시면 감사하겠습니다.

 

 

참고 :

https://developer.mozilla.org/ko/docs/Learn/JavaScript/Asynchronous/Introducing

https://www.youtube.com/watch?v=fsmekO1fQcw&list=PLKo27PPIMBsw3vvD05vs-XgAiKQ5SMFmj&index=47 

https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/

https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke#syntax