이벤트 루프

Seunghyunkim1·2020년 7월 7일

프론트엔드

목록 보기
7/8

#위코드 #wecode

참고 https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf

자바스크립트 엔진

V8엔진
자바스크립트 엔진 중 유명한 것이 구글의 V8엔진입니다. V8엔진은 크롬과 노드js에서 사용됩니다.

V8엔진은 크게 두 부분으로 구성됩니다.

  • 메모리힙(Memory Heap): 메모리할당이 이루어지는 곳입니다
  • 콜스택(Call Stack): 코드가 실행되면서 스택 프레임이 쌓이는 곳입니다
    자바스크립트가 단일 쓰레드(single-threaded)이고 콜백 큐(callback queue) 이용.

런타임

브라우저에는 거의 모든 자바스크립트 개발자가 사용하는 API가 있습니다(예를 들면 setTimeout과 같은 것들). 하지만 이들은 엔진이 제공하는 것이 아닙니다.
그럼 어디서 구현?

자바스크립트의 엔진이 중요하긴 하지만 엔진만으로 모든 것이 이루어지는 것은 아닙니다. 브라우저가 제공하는 웹 API라는 것도 있어서 DOM, AJAX, setTimeout등이 여기에 포함됩니다.

콜스택

콜스택이란 프로그램의 메모리 영역을 스택영역, 힙영역, 데이터영역, 코드영역중 스택영역
자바스크립트는 싱글 쓰레드(single-threaded) 프로그래밍 언어이고, 다시 말하면 콜스택이 하나라는 뜻입니다. 따라서 한 번에 하나의 일만 발생

콜스택은 기본적으로 우리가 프로그램의 어디에 있는지를 기록하는 자료 구조
우리가 함수 안으로 들어가는 순간(호출하는순간) 해당 함수를 이 스택의 제일 위에 놓게됩니다.

이 함수가 호출된후 다시 돌아오면 제일위에 놓였던 함수는 제거.

콜스택 쌓이는 예시

function multiply(x, y) {
    return x * y;
}
function printSquare(x) {
    var s = multiply(x, x);
    console.log(s);
}
printSquare(5);

엔진이 이 코드의 수행을 시작할 때 콜 스택은 비어있는 상태이고,
코드가 진행될때 단계예시

콜스택의 각각은 스택프레임(Stack Frame)이라고 명칭
스택프레임이 예외가 발생했을때 스택트레이스가 만들어진다.

스택트레이스 예시

function foo() {
    throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
    foo();
}
function start() {
    bar();
}
start();

만약 이 코드를 크롬에서 실행하면(이 파일이 foo.js라는 파일에 들어 있다고 했을 때) 다음과 같은 스택트레이스가 생성됩니다.

스택 날림(Blowing the stack)

콜스택의 최대 크기에 다다랐을 때 나타납니다.
특히 재귀 함수를 면밀히 테스트하지 않은 경우 일어남.

예시

function foo() {
    foo();
}
foo();

자바스크립트 엔진이 이 코드를 실행할 때는 먼저 foo 함수를 호출하는 것부터 시작 하지만 이 함수는 재귀적이어서 종료 조건 없이 함수 자신을 계속해서 호출한다. 결과적으로, 아래과같이, 함수가 호출되는 단계마다 동일 함수가 콜스택에 반복 추가된다.

콜스택의 수가 실재 콜스택의 크기를 넘게되면 브라우저는 에러를 던져버린다.

단일 쓰레드는 제한점도 많습니다. 자바스크립트는 콜스택이 하나입니다.

단일 쓰레드에서 특정 코드 실행이 늦어진다면?

단일쓰레드와 멀티쓰레드 차이??

동시성과 이벤트 루프

만약 콜스택 내에 수행시간이 긴 함수가 있다면???

예를들어, 브라우저의 자바스크립트로 복잡한 이미지를 변형을 해야 할때

콜스택에 수행할 함수가 있으면 브라우저는 아무것도 할 수 없다는 게 문제. 바로 ‘블록킹'되는 것입니다. 브라우저는 렌더링을 할 수도 없고 다른 코드를 수행할 수도 없고 그야말로 끼어 있는 상황이 되는 것입니다. 브라우저에서 UI가 표현 될때 문제가 생기고, 브라우저는 콜스택 내의 많은 작업을 수행하면서 꽤 긴 시간 동안 응답이 없을 수 있습니다. 그러면 대부분의 브라우저에서 에러를 일으키고 사용자에게 해당 페이지를 닫을지 물어보기도 합니다. 응답없음..


단일스레드의 단점? 해결책

해결책?

뷰티풀 UI를 막지 않고 브라우저가 응답없음 상태에 빠지게 하지 않으면서도 무거운 코드를 수행하려면? 해결책은 비동기 콜백(asynchronous callbacks).

자바스크립트를 처음 접하는 대부분의 개발자들이 이해하기 어려워 하는 것 중 하나는 이 ‘나중'이라는 것이 꼭 ‘지금’의 바로 다음이거나 꼭 시간 상으로 뒤에 일어나는 것을 말하는 것은 아니다!!. 정의상 ‘지금' 완료할 수 없는 작업은 비동기적으로 수행된다. 그러면 위에 언급한 블록킹 동작(blocking behavior)을 없앨 수 있습니다.

V8 엔진에 따른 JS 작동원리: 나중에 볼것

이벤트루프 사용 이유, 배경

우리가 만든 프로그램의 코드를 실행하라고 JS엔진에게 명령하는 것은 누구일까요? 실제로 JS엔진은 고립되어서 수행되는 것이 아니고 호스팅 환경 내에서 수행된다. 대부분의 개발자들에게 그 호스팅환경은 웹 브라우저 혹은 Node.js일 것입니다.

이러한 모든 환경에서 적용되는 최대공약수는 호스팅 환경에 내장된 메커니즘인 이벤트루프입니다. 이것은 시간의 흐름에 따라 코드의 수행을 처리하며 그 때마다 JS엔진을 작동시킵니다.

이는 JS엔진은 단지 임의의 JS코드에 대한 온디멘드 실행 환경이라는 뜻입니다. 각 이벤트(JS코드의 수행)를 스케쥴링하는 것은 그것을 둘러싸고 있는 환경입니다.

따라서, 예를 들어 서버에서 데이터를 가져오기 위해 Ajax 요청을 하는 자바스크립트 프그램이 있다고 했을 때 함수 내에 “응답” 코드를 설정하게 되는데 이는 JS엔진이 호스팅 환경에게 이와 같이 말하는 것과 같습니다.

“이봐 나는 이제 코드 수행을 중지시킬 거야. 하지만 자네는 네트워크 요청이 모두 끝나면 가져온 데이터를 이용해 이 함수를 호출(call back)해주게.”

그러면 브라우저는 네트워크로부터의 응답에 대기하고 있다가 반환할 거리가 생겼을 때 콜백 함수가 수행될 수 있도록 이벤트 루프에 삽입하는 것으로 스케쥴링을 수행합니다.

이벤트루프

참고

JS Engine

자바스크립트 엔진은 Memory Heap 과 Call Stack 으로 구성되어 있다.
(가장 유명한 것이 구글의 V8 Engine)
자바스크립트는 단일 스레드 (sigle thread) 프로그래밍 언어인데,
이 의미는 Call Stack이 하나 라는 이야기이다.
(멀티가 되지 않고, 하나씩 하나씩 처리한다는 의미!)

  • Memory Heap : 메모리 할당이 일어나는 곳
    (ex, 우리가 프로그램에 선언한 변수, 함수 등이 담겨져 있음)
  • Call Stack : 코드가 실행될 때 쌓이는 곳. stack 형태로 쌓임.
    Stack(스택) : 자료구조 중 하나, (LIFO, Last In First Out)의 룰을 따른다.

Web APIs

그림의 오른쪽에 있는 Wep API는 JS Engine의 밖에 그려져 있다.
즉, 자바스크립트 엔진이 아니다.
Web API 는 브라우저에서 제공하는 API 로, DOM, Ajax, Timeout 등이 있다.
이것들은 개발자가 접근할 수 없는 쓰레드이며 호출만 가능한 명령어들이다. 여기가 브라우저에서 동시성이 확보되는 지점이다.
Call Stack에서 실행된 비동기 함수는 Web API를 호출하고,
Web API는 콜백함수를 Callback Queue에 밀어 넣는다.

Callback Queue

비동기적으로 실행된 콜백함수가 보관 되는 영역이다.
예를 들어 setTimeout에서 타이머 완료 후 실행되는 함수(1st 인자),
addEventListener에서 click 이벤트가 발생했을 때 실행되는 함수(2nd 인자) 등이 보관된다.

Queue(큐) : 자료 구조 중 하나, 선입선출(FIFO, Frist In Frist OUT)의 룰을 따른다.

그외 Queue들

이벤트루프란..


Event Loop는 Call Stack과 Callback Queue의 상태를 체크하여,
Call Stack이 빈 상태가 되면, Callback Queue의 첫번째 콜백을 Call Stack으로 밀어넣고, 해당 이벤트 실행.
이러한 반복적인 행동을 틱(tick) 이라 부른다.
각각의 이벤트는 단순히 함수를 실행하는 것입니다.

(Job Queue 확인할것)

이벤트루프 에시

console.log('Hi');
setTimeout(function cb1() { 
    console.log('cb1');
}, 5000);
console.log('Bye');

위의 코드가 실행될때의 order















그림요약

다시가서 읽어볼것

콜백

콜백은 자바스크립트 프로그램에서 비동기를 관리하고 표현하는데 가장 많이 쓰이는 방법입니다. 콜백은 자바스크립트 에서 비동기 패턴의 가장 근본이라 할 수 있다.
콜백에도 단점이 있고, 더 나은 비동기 패턴을 찾기 위한 블로그들이 많지만 어떤 추상화든 그 내부가 어떻게 돌아가는지를 이해하지 않고서는 효과적으로 사용할 수 없을 것 같은 고통스러운 자바스크립트..

더 정교한 비동기 패턴들 공부하자..

중첩 콜백( 콜백지옥 )

listen('click', function (e){
    setTimeout(function(){
        ajax('https://api.example.com/endpoint', function (text){
            if (text == "hello") {
	        doSomething();
	    }
	    else if (text == "world") {
	        doSomethingElse();
            }
        });
    }, 500);
});

세 개의 함수들이 서로 중첩되어 있으며 각각은 비동기 시리즈에서 하나의 단계를 나타냅니다.

먼저 우리는 ‘click’ 이벤트를 기다립니다. 그리고 타이머가 실행되기를 기다립니다. 그 다음에는 Ajax 반응이 돌아오기를 기다리는데 그 때가 되면 이 모든 것이 반복됩니다.

위의 코드의 비동기성을 순차적인 단계로 매핑하면 아래와 같다.

listen('click', function (e) {
	// ..
});

setTimeout(function(){
    // ..
}, 500);

ajax('https://api.example.com/endpoint', function (text){
    // ..
});

if (text == "hello") {
    doSomething();
}
else if (text == "world") {
    doSomethingElse();
}

프로미스 + async/await

이렇게 비동기 코드를 순서대로 표현하는 것이 더 자연스럽다.
이렇게 하기위해서 프로미스사용

0개의 댓글