Session 16_0B. 비동기를 구현하는 문법

*🔐Study Keyword :

✅비동기를 처리하는 3가지 방식인 🔑콜백 함수와 🔑Promise 그리고 🔑Async/Await를 직접 코드로 쳐보며 어떠한 차이가 있고 어떻게 사용할지 자알 알아두자!

<이번 섹션에서 다룰 목차>

  • 동기와 비동기 & Node.Js의 정의
  • Callback
  • Promise
  • async/await
  • Event Loop
  • Web API
  • Task Queue
  • Call Stack

  • 자바스크립트 엔진은 단 하나의 실행 컨텍스트 스택을 갖게 되고 한 번에 하나의 태스크만 실행할 수 있는 싱글 스레드 방식으로 동작한다.

  • 비동기 처리를 구현하는 방식에 대해 알기 전에 위 문장에 대한 이해가 있어야한다.
    따라서싱글 스레드 방식은 결국 한 번에 하나의 태스크만 실행할 수 있어서 처리에 시간이 걸리는 태스크를 실행하는 경우 블로킹(작업 중단)이 발생한다.

<script>
function sleep(func, delay) {
  const delayUntill = Date.now() + delay
  while(Date.now() < delayUntill);
  func()
}
function foo(){
  console.log('foo')
}
function bar(){
  console.log('bar')
}
sleep(foo, 3 *1000)
// sleep 함수 3초 후에 foo 함수 호출
bar()
// bar함수는 sleep 함수 실행 종료 후에 호출되므로 3초 이상 호출 되지 못하고 블로킹 된다.
</script>
  • 위 코드처럼 현재 실행 중인 태스크가 종료할 때까지 다음에 실행될 태스크가 대기하는 방식을 동기 처리라고한다.

1. 콜백함수

-WHAT IS❓

  • 자바스크립트는 비동기 처리를 위한 하나의 패턴으로 콜백함수를 사용한다.
    비동기 함수비동기 처리 결과를 외부에 반환할 수 없고, 상위 스코프의 변수에 할당할 수 도 없기에 비동기 함수의 처리 결과(서버의 응답)에 대한 후속 처리는 비동기 함수 내부에서 수행해야 한다.
  • 이때 비동기 함수에 비동기 처리 결과에 대한 후속 처리를 수행하는 콜백 함수를 전달하는 것이 일반적이다.
  • 그러나 콜백 패턴은 콜백 헬🔥로 인해 1> 가독성이 나쁘고 2> 비동기 처리 중 발생한 에러의 처리가 곤란하여 여러 비동기 처리를 한 번에 처리하는데 있어 한계를 가진다.

콜백 함수의 단점

  • WHY❔❕
  • 콜백 헬이라는 단어에 대해서 많이 들어봤을 거다.
    콜백 헬이란 콜백 함수를 통해 비동기 처리 결과에 대한 후속 처리를 수행하는 비동기 함수가 비동기 처리를 가지고 또 다시 비동기 함수를 호출하면 콜백 함수 호출이 중첩되어 복잡도가 높아지는 현상을 말한다.

1> 가독성 나쁜 예시

- 이건 뭐 장풍도 아니고... 콜백 함수로 비동기 처리 결과에 대한 후속 처리를 수행하기 위해 콜백 함수를 전달하면 저렇게 깊어지는 현상이...

2> 에러 처리의 한계

<script>
try {
  setTimeout(() => {
    throw new Error('err!');}, 1000);
} catch(e) {
  // 에러를 캐치하지 못한다.
  console.error('catch err:', e)
}
</script>
  • 비동기 처리를 위한 콜백 패턴의 가장 심각한 문제는 에러 처리가 곤란하다는 것이다.

  • 위 코드에서 1> setTimeout가 호출되면 콜스택에 푸시되어 실행되는데 setTimeout은 비동기 함수이므로 콜백 함수가 호출 되는 것을 기다리지 않고 즉시 종료되어 콜스택에서 제거된다.
    2> 이후 타이머가 만료되면 setTimeout 함수의 콜백 함수는 태스크 큐로 푸시되고 콜스택이 비어졌을 때 이벤트 루프에 의해 콜 스택으로 푸시되어 실행된다.
    3> 중요한건 에러는 호출자 방향을 전파되어 즉, 콜 스택의 아래방향으로 전파된다는 것이다. 하지만 위 코드에선 setTimeout 함수의 콜백 함수를 호출 한 것은 setTimeout 함수가 아니므로 setTimeout 함수의 콜백 함수가 발생시킨 에러는 catch 블록에서 캐치되지 않는다.


2. Promise

-WHAT IS❓

  • ES6에서 비동기 처리를 위한 또 다른 패턴으로서 Promise를 도입했다.
    Promise는 전통적인 콜백 패턴이 가진 단점을 보완하며 비동기 처리 시점을 명확하게 표현할 수 있다는 장점을 가진다.

2_1. Promise 생성방식

  • Promise 생성자 함수new 연산자와 함께 호출하면 프로미스(객체)를 생성한다.
  • Promise 생성자 함수는 비동기 처리를 수행할 콜백 함수를 인수로 전달받는데 이 콜백 함수는 resolvereject 함수를 인수로 전달받는다.
<script>
componentDidMount() {
    const promiseGet = (url) => {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.send();
        xhr.onload = () => {
          // onload 이벤트 핸들러가 비동기로 동작한다.
          if (xhr.status === 200) {
            // 서버의 응답을 전달 받으면 resolve 함수를 호출
            resolve(JSON.parse(xhr.response));
          } else {
            reject(new Error`${xhr.status}`());
          }
        };
      });
    };
    const response = promiseGet("https://jsonplaceholder.typicode.com/users/1");
    console.log(response);
}
</script>
  • Promise 생성자 함수가 인수로 전달받은 콜백 함수 내부에서 비동기 처리를 수행한다. 이때 비동기 처리를 성공하면 콜백 함수의 인수로 전달받은 resolve 함수를 호출하고 비동기 처리가 실패하면 reject 함수를 호출한다.

2_2. Promise의 상태정보

  • Promise는 현재 비동기 처리가 어떻게 진행되고 있는지를 나타내는 상태 정보를 갖는다.
  • 생성된 직후의 프로미스는 기본적으로 pending 상태이고 이후 비동기 처리가 수행되면 비동기 처리 결과에 따라서 프로미스의 상태가 변경된다.
  • 프로미스의 상태는 resolve 또는 reject 함수를 호출하는 것으로 결정된다.
  • 비동기 처리 성공 시: resolve 함수 호출 => 프로미스를 fullfilled 상태로 변경
  • 비동기 처리 실패 시: reject 함수 호출 => 프로미스를 rejected 상태로 변경

2_3. Promise의 비동기 처리 결과

  • 비동기 처리 상태와 더불어 비동기 처리 결과도 상태로 갖게 된다.

  • fullfilled 또는 rejectet 상태를 settled 상태로 pending이 아닌 상태로서 비동기 처리가 수행된 상태를 의미하고 settled가 되면 다른 상태로 변화할 수 없다.
  • 따라서 프로미스는 비동기 처리 상태와 처리 결과를 관리하는 객체라고 할 수 있다.

2_3. 프로미스의 후속 처리 메서드

  • 프로미스의 비동기 처리 상태가 변화하면 이에 따른 후속 처리를 해야한다.
    이를 위해 후속 메서드 then,catch,finally를 제공한다.
  • 프로미스의 비동기 처리 상태가 변화하면 후속 처리 메서드에 인수로 전달한 콜백함수가 선택적으로 호출되고 이때 후속 처리 메서드의 콜백 함수에 프로미스의 처리 결과가 인수로 전달된다.
  • 모든 후속 처리 메서드는 프로미스를 반환하며 비동기로 동작한다.

프로미스의 후속 처리 메서드

1> then*

  • then 메서드는 두개의 콜백 함수를 인수로 전달받는다.
    첫 번째 콜백함수는 비동기 처리 성공 시 호출되는 성공 처리 콜백 함수, 두 번재 콜백 함수는 비동기 처리 실패 시 호출되는 실패 처리 콜백 함수이다.
    <script>
    // fullfilled
    new Promise(resolve=>resolve('fullfilled'))
    .then(v => console.log(v),e => console.log(error(e)))
    // rejected
    new Promise((_, reject)=>reject(new Error('rejected')))
    .then(v => console.log(v), e => console.error((e)))
    </script>
-  주의 할 점은 then 메서드는 언제나 프로미스를 반환한다는 것으로 만약 then 메서드의 콜백 함수가 프로미가 아닌 값을 반환하면 그 값을 암묵적으로 resolve 또는 reject하여 프로미스를 생성해 반환한다.

2> catch

  • catch 메서드는 한 개의 콜백 함수를 인수로 전달 받고 catch 메서드의 콜백 함수는 프로미스가 rejected 상태인 경우에만 호출된다.
<script>
new Promise((_, reject)=>reject(new Error('rejected')))
.catch(e => console.log(e)
</script>
  • catch 메서드는 then과 동일하게 동작한다. 따라서 언제나 프로미스를 반환한다.

3> finally

  • finally 메서드는 한 개의 콜백 함수를 인수로 전달 받는다. finally 콜백 함수는 성공과 실패 상관없이 무조건 한 번 호출된다.
  • finally메서드는 프로미스의 사태와 상관없이 공통적으로 수행해야할 처리 내용이 있을 시 유용하다.
    <script>
    new Promise(()=>{})
    .finally(() => console.log('finally'))
    </script>
- 프로미스로 구현한 비동기 함수 get을 사용해 후속처리를 구현한 코드

🔅참고🔅)
💡TIP)

*💡conclusion

  • 비동기 처리 함수를 왜! 언제! 어떻게! 쓰면 좋은지를 잘 알아두자

#📑Study Source

  1. 딥다이브 자바스크립트 - 비동기 part

좋은 웹페이지 즐겨찾기