비동기(Asynchronous)

Overloper·2022년 6월 27일
1
post-thumbnail

📌 동기와 비동기의 개념

동기(Synchronous)

동기 방식은 서버에서 요청을 보냈을 때 응답이 돌아와야 다음 동작을 수행 할 수 있다.
먼저 처리하는 작업이 있으면 다음 작업은 대기해야한다

비동기(Asynchronous)

비동기 방식은 요청을 보냈을 때 어떤 상태와 상관없이 다음 동작을 수행 할 수 있다.
작업이 시작되면 동시에 다른 작업도 실행된다.

📌 동기처리와 비동기 처리

const foo = () => {};
const bar = () => {};

foo();
bar();

✅ 함수를 호출하면 함수 코드가 평가되어 함수 실행 컨텍스트 생성.
➡️ 생성된 함수 실행 컨텍스트는 콜 스택으로 푸시
➡️ 코드가 실행되고 실행이 종료되면 함수 실행 컨텍스트는 콜 스택에서 팝되어 제거

즉 함수의 실행의 시작은 콜 스택에 함수 실행 컨텍스트에 푸시가 된 지점이다.

함수가 호출된 순서대로 순차적으로 실행되는 이유는 호출 순서대로 함수 실행 컨텍스트가 콜 스택에 푸시되기 때문이다

🛑 자바스크립트의 엔진은 단 하나의 콜 스택에만 존재한다. 🛑

이는 2개 이상의 함수를 실행할 수 없다는 것을 뜻한다. 콜 스택의 최상위 요소인 "실행중인 실행 컨텍스트" 를 제외한 모든 실행 컨텍스트는 모두 실행 대기 중인 태스크(Task) 이다. ( Task란 작업단위의 실행 단위)
태스크는 현재 실행 컨텍스트가 팝되어 제거되면 실행된다.

자바스크립트의 엔진은 한 번에 하나의 태스크를 실행할수 있는 싱글스레드(Single thread) 방식으로 동작한다.
그렇기 때문에 처리에 시간이 걸리는 태스크일 경우 블로킹(Blocking)이 생긴다.

책에 나와있는 예제를 확인해봅시다.

function sleep(func, delay) {
	// Date.now() 는 현재 시간을 숫자로(ms)로 반환
    const delayUntil = Date.now() + delay;
    
    // 현재 시간에 delay를 더한 delayUntil이 현재 시간보다 작으면 계속 반복한다.
    while (Date.now() < delayUntil);
    // 일정 시간이 경과한 후 콜백함수를 실행한다.
    func();
}

function foo() {
	console.log('foo');
}

function bar() {
	console.log('bar');
}

sleep(foo, 3 * 1000);

위 예제의 sleep 함수는 3초 후 foo 함수를 호출, bar 함수는 sleep 함수의 실행이 종료된 후에 호출 되므로 3초이상 호출 되지 못하고 블로킹 됩니다.

이처럼 현재 실행중인 태스크가 종료할 때 까지 다음에 실행될 태스크가 대기하는 방식을 동기 처리 라고 부르기로 했다.

위에 예제를 비슷한 타이머 함수인 setTimeout을 사용하겠습니다.

function foo() {
	console.log('foo');
}

function bar() {
	console.log('bar');
}

setTimeout(foo, 3 * 1000);
bar();

setTimeout는 sleep 함수와 유사하게 일정시간이 지난 뒤 콜백 함수를 호출하지만 setTimeout 함수 이후에 있는
태스크를 블로킹하지 않고 곧바로 실행시킨다. 이처럼 현재 실행 중인 태스크가 종료되지 않은 상태라해도
다음 태스크를 곧바로 실행하는 것을 비동기 처리 라고 부르기로 했다.

동기 처리는 태스크를 순서대로 처리하는 방식으로 실행 순서가 정해진다는 보장이 되지만 다른 태스크들이 블로킹되는 단점이 있다.
비동기 처리는 태스크가 끝나지 않아도 다른 태스크를 실행한다는 점이 있지만 실행 순서가 정해져 있지 않다.


전통적으로 비동기 함수는 콜백 패턴을 사용합니다.
비동기 처리를 위한 콜백은 콜백 헬(aka.콜백 지옥)을 발생시켜 가독성이 나빠지고 예외 처리가 많이 어려워진다.

대표적인 비동기 처리 방식은 setTimeout, setInterval, HTTP 요청, 이벤트 핸들러는 비동기 방식으로 동작한다.

📌이벤트 루프와 태스크 큐

위에서 언급된 내용으로는 자바스크립트는 싱글 스레드로 동작한다는 내용이 있었다.
싱글 스레드는 하나의 태스크만 처리할 수 있다는 것을 의미 한다. 하지만 브라우저가 동작하는 것을 보면
다양한 태스크가 동시에 처리되는 것을 볼 수 있다.
이처럼 자바스크립트의 동시성(concurrency)을 지원하는게 이벤트 루프 이다.

이벤트 루프는 브라우저에 내장된 기능 중 하나이다.

구글의 V8 자바스크립트 엔진을 비롯한 대부분의 자바스크립트 엔진은 크게 2개의 영역으로 구분할 수 있다.

  • 콜 스택

이처럼 콜 스택과 힙으로 구성된 자바스크립트 엔진은 단순히 태스크 요청이 되면 콜 스택을 통해 요청된 작업을 순차적으로 실행해 준다. 비동기 처리에서 소스코드의 평가와 실행을 제외한 모든 처리는 자바스크립트 엔진을 구동하는 브라우저 혹은 Node.js 가 처리 해준다.

예를 들어 setTimeout은 비동기 처리를 하는데 콜백 함수의 평가와 실행은 자바스크립트가 해주고
호출 스케쥴링을 위한 타이머 설정이나 콜백 함수 등록은 브라우저도 혹은 Node.js가 해준다. 이를 위해 브라우저는
태스크 큐와 이벤트 루프를 제공한다.

  • 태스크 큐(task queue)
    setTimeout이나 setInterval과 같은 비동기 함수의 콜백 함수 또는 이벤트 핸드러가 일시적으로 보관되는 영역.
    태스크 큐와는 별도로 프로미스의 후속 처리 메서드의 콜백 함수가 일시적으로 보관되는 마이크로태스크 큐도 있다.
  • 이벤트 루프(event loop)
    이벤트 루프는 콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지, 그리고 태스크 큐에 대기 중인 함수(콜백 함수) 가 있는지 반복해서 확인한다, 만약 콜 스택이 비어 있고 태스크 큐에 대기 중인 함수가 있다면 이벤트 루프는 순차적으로 태스크 큐에 대기 중인 함수를 콜 스택으로 이동한다. 이때 콜 스택으로 이동한 함수는 실행.
    즉 태스크큐에 일시 보관된 함수들은 비동기 처리로 동작.

정리!

비동기 함수인 setTimeout()의 콜백 함수는 태스크 큐에 푸시되어 대기하다가 콜 스택이 비게 되면,
전역 코드 및 명시적으로 호출된 함수가 모두 종료되면 비로소 콜 스택에 푸시되어 실행된다.

자바스크립트는 싱글스레드 방식으로 동작한다. 이때 싱글스레드 방식으로 동작하는 것은 브라우저가 아니라
브라우저에 내장된 자바스크립트 엔진인 것을 명확하게 알아야 한다. 모든 자바스크립트 코드가 자바 스크립트 엔진에서
싱글 스레드 방식으로 동작한다면 자바스크립트는 비동기로 동작할 수 없다. 즉 자바스크립트 엔진은 싱글 스레드로 동작하지만 브라우저는 멀티 스레드로 동작한다.

profile
세계최초 샤머니즘형 프로그래머🛐

0개의 댓글