JS | 비동기 처리

Jelloruby·2021년 5월 31일
0

Javascript

목록 보기
5/7
post-thumbnail

1. 동기 처리와 비동기 처리

✔ 동기 처리 : 특정 코드의 태스크가 종료할 때까지 다음에 실행될 태스크가 대기하는 방식

  • 장점 : 실행 순서가 보장된다.
  • 단점 : 앞선 태스크가 종료할 때까지 이후 태스크들이 블로킹 된다.

✔ 비동기 처리 : 특정 코드의 태스크가 종료 되지 않은 상태라 해도 멈추지 않고 다음 코드를 곧바로 실행하는 방식

  • 장점 : 블로킹이 발생되지 않는다.
  • 단점 : 실행 순서가 보장되지 않는다.

2. 이벤트 루프와 태스크 큐

자바스크립트는 싱글 스레드 언어이기 때문에 한 번에 하나의 태스크만 처리할 수 있다.
하지만 브라우저가 동작하는 것을 살펴보면 많은 태스크가 동시에 처리되는 것처럼 느껴진다.
이와같이 자바스크립트의 동시성을 지원하는 것이 바로 이벤트 루프(event loop)이다.


비동기 함수인 setTimeout의 콜백함수는 태스크 큐에 푸시되어 대기하다가 콜 스택에 호출된 함수들이 모두 종료될 때 푸시되어 실행된다.

객체가 저장되는 메모리 공간이다.

콜 스택

실행 컨텍스트가 추가되고 제거되는 스택 자료구조이다.

자바스크립트 엔진

브라우저 안에는 힙과 콜 스택으로 구성된 자바스크립트 엔진이 존재한다. 요청된 태스크를 콜 스택을 통해 순차적으로 실행한다.
비동기 처리에서 코드의 평가와 실행을 제외한 모든 처리는 브라우저 또는 Node.js가 담당한다. 즉, 브라우저는 멀티 스레드로 동작하지만 자바스크립트 엔진은 싱글 스레드로 동작한다.

마이크로태스크 큐, 태스트 큐

마이크로태스크 큐에는 프로미스의 후속 처리 메서드의 콜백 함수가 저장되고, 태스크 큐에는 비동기 함수의 콜백 함수나 이벤트 핸들러가 저장된다.
이벤트 루프는 마이크로 큐를 우선적으로 확인하고, 콜백 함수가 있다면 이를 먼저 콜 스택에 담는다. 그리고 더이상 처리해야할 함수가 없다면 태스크 큐를 확인한 후 처리한다.

이벤트 루프

콜 스택에 현재 실행중인 실행 컨텍스트가 있는지, 마이크로 큐 또는 태스크 큐에 대기중인 함수가 있는지 확인한다. 만약 콜 스택이 비어있다면, 대기중인 함수를 순차적(*FIFO)으로 콜 스택으로 이동시킨다.

스택의 구조는 후입선출(LIFO, Last-In-First-Out), 큐의 구조는 선입선출(FIFO, First In First Out)이다.

3. 프로미스

자바스크립트는 비동기 처리를 위해 콜백 함수를 사용한다. 하지만 전통적인 콜백 패턴은 콜백 지옥으로 인해 가독성이 나쁘고, 여러 개의 비동기 처리를 한번에 처리하는 데도 한계가 있다.
프로미스는 이와 같은 문제를 해결하기 위해 ES6부터 도입된 패턴이다.

콜백 지옥(Callback Hell) 이란?
비동기 함수는 비동기 처리 결과를 외부에 반환할 수 없고, 상위 스코프의 변수에 할당할 수도 없다. 즉, 비동기 함수의 처리 결과에 대한 후속 처리는 비동기 함수 내부에서 수행해야 한다.
후속 처리는 콜백 함수를 전달하는 것이 일반적이며, 여러 개의 비동기 처리를 해야할 경우에는 복잡도가 높아지는 현상이 발생한다. 이를 콜백 지옥이라고 한다.

프로미스의 상태 정보

✔ pending : 비동기 처리가 아직 수행되지 않은 상태, 프로미스 생성 직후 기본 상태
✔ fulfilled : 비동기 처리가 성공한 상태, resolve 함수 호출
✔ rejected : 비동기 처리가 실패한 상태, reject 함수 호출

프로미스에 전달되는 실행 함수는 자바스크립트가 자체적으로 제공하는 콜백함수 resolve와 reject를 인수로서 전달받는다.

✔ resolve : 비동기 처리가 성공했을 때, 처리 결과 value와 함께 호출된다.
✔ reject : 비동기 처리가 실패했을 때, 에러 객체 error와 함께 호출된다.

프로미스의 후속처리

프로미스의 비동기 처리 상태가 변화하면 후속 처리가 필요하다. 이를 위해서 프로미스는 후속 메서드 then, catch, finally를 제공한다.

// then
new Promise(resolve => resolve('fulfilled'))
    .then(value => console.log(value))
// catch
new Promise((_, reject) => reject(new Error('rejected'))) 
    .catch(error => console.log(error))
new Promise((_, reject) => reject(new Error('rejected')))
    .catch(null, error => console.log(error))

then

✔ 항상 프로미스를 반환하며, 반환된 프로미스룰 가지고 연속적으로 체이닝 할 수 있다.
✔ 콜백 함수가 프로미스가 아닌 값을 반환한 경우에는 그 값을 프로미스로 래핑하여 반환한다.
✔ 프로미스가 fullfilled와 rejected 상태인 경우에 콜백함수가 호출된다.

catch

✔ 항상 프로미스를 반환하며, 반환된 프로미스룰 가지고 연속적으로 체이닝 할 수 있다.
✔ 프로미스가 rejected 상태인 경우에만 호출된다.
✔ then에 null 또는 undefined를 함께 호출하는 것과 동일하게 작용한다.

프로미스와 마이크로태스크 큐

위의 '이벤트 루프와 태스크 큐'에서 살펴봤듯이 마이크로태스크 큐는 프로미스의 후속 처리 메서드의 콜백 함수를 저장한다.




📚 참고

0개의 댓글

관련 채용 정보