이벤트 루프

Yohan Kim·2023년 4월 3일
0

평소 헷갈리던 개념을 chat gpt와 함께 정리해보기로 했다.

Q: Javascript의 이벤트루프에 대해 자세히 설명해줘

A: 자바스크립트는 단일 스레드로 동작하는 언어입니다. 이는 하나의 실행 컨텍스트(Execution Context)만 실행할 수 있다는 것을 의미합니다. 하지만 자바스크립트는 비동기적인 동작을 지원하기 위해 이벤트 루프(Event Loop)를 사용합니다.
이벤트 루프는 자바스크립트 엔진의 콜 스택(Call Stack)과 태스크 큐(Task Queue)를 기반으로 동작합니다. 콜 스택은 현재 실행 중인 실행 컨텍스트들이 쌓이는 스택이고, 태스크 큐는 비동기적으로 실행될 콜백 함수들이 대기하는 큐입니다.

이벤트 루프는 다음과 같은 순서로 동작합니다.
1. 콜 스택이 비어있는지 확인합니다.
2. 콜 스택이 비어있다면, 태스크 큐에서 콜백 함수를 꺼내 콜 스택에 넣습니다.
3. 콜 스택에 콜백 함수가 쌓이면 실행됩니다.
4. 콜백 함수가 실행되면, 해당 함수의 실행 컨텍스트가 콜 스택에서 제거됩니다.

1번으로 돌아가 콜 스택이 다시 비어있는지 확인합니다.
이렇게 이벤트 루프를 통해 자바스크립트는 비동기적인 동작을 지원하면서도 단일 스레드로 동작할 수 있습니다. 이벤트 루프는 콜 스택이 비어있을 때만 태스크 큐에서 콜백 함수를 꺼내 실행하기 때문에, 자바스크립트에서는 데드락(Deadlock)과 같은 상황이 발생하지 않습니다.


내가 이해한 내용

컴퓨터는 메모리의 주소를 읽어서 한줄씩 실행하는 것이고 이걸 하나의 실행흐름이라고 생각하면 된다.
프로그램이 프로세스 단위로 실행되고 하나의 프로세스에서 여러 개의 스레드를 통해 동시에 여러가지 작업들을 할 수 있다. 프로세스간 통신과, 스레드간 통신이 조금 다르다. 전환하는 비용도 프로세스가 큰 편!

자바스크립트가 단일 스레드라는 말은, 1개의 프로세스 - 1개의 스레드를 가진다는 의미이고, 이는 오직 한가지의 실행 흐름만을 가진다는 뜻이다.

자바스크립트의 모든 동작은 컨택스트 단위로 이루어지는데 비동기 작업을 위해 이벤트 루프를 사용한다고 이해했다.

추가로 궁긍한 사항

그러면 콜스택은 어떤 일을 하는거지? 콜스택에 비동기함수와 동기함수가 다르게 쌓이는 건가?


Q: 콜스택에 동기적인 함수들은 쌓이지 않나?

네, 콜스택에 동기적인 함수들도 쌓입니다. 콜스택은 실행되는 함수들이 쌓이는 스택으로, 함수가 실행되면 해당 함수의 실행 컨텍스트가 콜스택에 쌓이게 됩니다. 즉, 자바스크립트에서는 동기적으로 실행되는 함수들도 모두 콜스택에 쌓이게 됩니다.

예를 들어, 다음과 같은 코드가 있다고 가정해보겠습니다.

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

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

bar();

이 코드에서 bar() 함수가 실행되면 console.log('bar') 구문이 실행되고, 다음으로 foo() 함수가 실행됩니다. 이때, foo() 함수가 실행되면 해당 함수의 실행 컨텍스트가 콜스택에 쌓이게 되고, foo() 함수가 끝나면 콜스택에서 해당 실행 컨텍스트가 제거됩니다. 그리고 다시 bar() 함수의 실행 컨텍스트가 콜스택에서 실행되며, console.log('bar') 구문이 끝나면 bar() 함수의 실행 컨텍스트도 콜스택에서 제거됩니다.

이처럼 동기적으로 실행되는 함수들도 콜스택에 쌓이고, 실행 컨텍스트가 제거되는 과정이 반복되며 자바스크립트는 실행됩니다.


내가 이해한 내용

콜스택이라는 것이 자바 같은 곳에서 사용하던 activation record와 같이 동작하는 거구나.
해당 개념은 어셈블리어로도 구현을 해봤기 때문에, 얼추 감을 잡을 수 있었다.

OS 내부에서 stack을 사용해서 함수의 실행을 관리하기 때문에 bar 이후에 foo를 호출할 수 있는 것이다.

내가 원한건 이게 아닌데 다시 질문을 해보도록 하자


Q: 그러면 비동기함수는 콜스택에 쌓이지만 비동기 작업이 끝나고 나서야 실행되니까 이벤트 루프 내부에서는 어떻게 이를 가능캐하는거야?

비동기 함수는 실행 중인 함수의 실행 컨텍스트가 콜스택에서 제거된 후, 태스크 큐에 콜백 함수로 등록됩니다. 즉, 비동기 함수 자체는 콜스택에서 실행됩니다. 그러나 비동기 함수가 반환되면, 해당 함수의 실행 컨텍스트는 콜스택에서 제거됩니다.

이후, 비동기 함수가 완료되면 등록된 콜백 함수가 태스크 큐에 삽입됩니다. 이벤트 루프는 콜 스택이 비어있을 때마다, 태스크 큐에서 새로운 콜백 함수를 꺼내와 콜 스택에 쌓아 실행합니다.

따라서 이벤트 루프는 콜 스택이 비어있을 때마다 태스크 큐에 있는 콜백 함수를 실행하는 방식으로 동작합니다. 이벤트 루프는 콜백 함수를 꺼내 실행하기 전에, 해당 함수를 실행하기 위한 환경(실행 컨텍스트)을 생성하고 콜 스택에 쌓습니다. 그리고 콜백 함수가 실행될 때까지 콜 스택에 있는 다른 함수들은 실행되지 않습니다. 즉, 이벤트 루프는 콜백 함수의 실행이 완료될 때까지 콜 스택을 비우지 않고, 콜백 함수의 실행이 완료될 때까지 다른 함수들의 실행을 블로킹합니다. 이를 통해 비동기적으로 동작하는 함수를 쉽게 구현할 수 있습니다.

내가 이해한 내용

아 비동기 함수와 , 동기 함수 모두 콜스택에 쌓이지만, 비동기의 경우는 바로 콜스택에서 제거되고 콜백함수를 등록한다는 거구나


Q: 요약하자면 비동기함수는 불리면 바로 실행되지않고, 컨텍스트가 제거되고 콜백함수가 태스크 큐에 삽입되고 나중에 테스크 큐로부터 시그널을 받는거야?

네, 맞습니다! 비동기 함수는 불려졌을 때 즉시 실행되지 않고, 해당 함수의 실행 컨텍스트가 콜 스택에서 제거됩니다. 그리고 비동기 작업이 완료되면 그 결과를 처리하기 위한 콜백 함수가 태스크 큐에 등록됩니다. 이후 이벤트 루프는 콜 스택이 비어있을 때마다 태스크 큐에서 새로운 콜백 함수를 꺼내와 콜 스택에 쌓아 실행합니다.

따라서 비동기 함수는 실행이 시작되면 먼저 해당 함수의 실행 컨텍스트가 콜 스택에서 제거되기 때문에, 다른 코드들이 먼저 실행될 수 있습니다. 이후 비동기 작업이 완료되면, 이벤트 루프는 콜 스택이 비어있을 때 해당 콜백 함수를 실행시킵니다. 이때 콜백 함수는 새로운 실행 컨텍스트를 만들어서 콜 스택에 올리고 실행됩니다.

내가 이해한 내용

하나의 실행 흐름 - 자바스크립트, 콘텍스트 단위의 실행
비동기 작업의 경우 콜스택에 쌓였다가 바로 사라지고 콜백함수를 등록한다.
이 후 콜스택이 비었을 경우, 콜백함수에서 가져와서 실행되어 비동기적으로 작동한다.

근데 어떻게 비동기 작업이 끝난지 아는거지? 콜스택이 비었을 때 콜백함수를 가져오는데, 만약 작업이 완료가 안되있으면 동작 못하지 않나?


Q:자바스크립트의 이벤트 루프가 비동기 작업이 끝났는지 어떻게 알 수 있을까?

자바스크립트 엔진은 비동기 작업이 완료되면 해당 작업에 대한 콜백 함수를 태스크 큐에 등록합니다. 이벤트 루프는 콜스택이 비어있을 때, 태스크 큐에서 다음 태스크를 가져와 콜스택에 추가합니다.

따라서 이벤트 루프는 비동기 작업이 완료되었는지를 알기 위해 태스크 큐에 등록된 콜백 함수를 확인합니다. 즉, 비동기 작업의 완료 여부는 해당 작업에 대한 콜백 함수가 태스크 큐에 등록됨으로써 이벤트 루프에게 알려지며, 이벤트 루프는 이를 감지하여 적절한 처리를 수행합니다.

예를 들어, setTimeout 함수는 지정된 시간이 경과하면 콜백 함수를 태스크 큐에 등록합니다. 이벤트 루프는 콜스택이 비어있을 때, 태스크 큐에서 등록된 콜백 함수를 가져와 실행합니다. 이렇게 태스크 큐에서 콜백 함수를 가져와 실행하는 과정을 통해 비동기 작업이 완료되었음을 이벤트 루프는 알 수 있습니다.

또한 Promise 객체를 사용한 비동기 작업에서는 then 메서드를 사용하여 비동기 작업이 완료되면 처리할 콜백 함수를 등록할 수 있습니다. 이벤트 루프는 then 메서드에 등록된 콜백 함수가 태스크 큐에 등록됨으로써 비동기 작업의 완료 여부를 감지하고, 적절한 처리를 수행합니다.

Q: 아 그러면 비동기 함수의 경우, 이 실행이 끝났을 경우를 알리는 내부 구현이 되있도록 설계되있는거야?

네, 대부분의 비동기 함수는 내부적으로 비동기 작업이 완료되었음을 나타내는 콜백 함수나 Promise 객체 등을 반환하도록 설계되어 있습니다. 이를 통해 비동기 작업의 완료 여부를 감지하고 적절한 처리를 수행할 수 있습니다.

예를 들어, XMLHttpRequest 객체를 사용한 AJAX 요청을 보내는 경우, onload 콜백 함수를 등록하여 비동기 작업이 완료되었음을 나타냅니다. Promise 객체를 사용한 비동기 작업의 경우, then 메서드를 사용하여 비동기 작업이 완료되면 처리할 콜백 함수를 등록할 수 있습니다.

이러한 내부 구현은 이벤트 루프가 비동기 작업의 완료 여부를 감지하고 적절한 처리를 수행하는 데 중요한 역할을 합니다.

Q:그러면 onload나 then 같은 경우 자바스크립트의 인터페이스를 상속해서, 이 함수들이 비동기작업이 끝났음을 알리는거야?

onload나 then은 자바스크립트 언어 자체에서 지원하는 메서드이며, 비동기 작업이 끝났을 때 해당 콜백 함수가 실행되도록 내부적으로 구현되어 있습니다. 이들 메서드는 자바스크립트의 인터페이스를 상속받은 것이 아니라, 해당 객체의 프로퍼티로 존재하며, 해당 프로퍼티에 함수를 등록함으로써 비동기 작업의 완료 여부를 감지할 수 있도록 지원됩니다.

XMLHttpRequest의 경우, onload은 XMLHttpRequest 객체의 프로퍼티로 존재하며, XMLHttpRequest 객체가 비동기 요청의 응답을 받으면 해당 콜백 함수가 실행됩니다. Promise 객체의 경우, then은 Promise 객체의 프로토타입에 정의된 메서드로, Promise 객체가 비동기 작업을 수행하면 해당 콜백 함수가 실행됩니다.

Q: onload, then 같은 경우는 언어와 함께 만들어진거고, settimeout도 똑같아?

setTimeout 메서드 역시 자바스크립트 언어 자체에 내장된 메서드로, 브라우저 환경에서 실행되는 자바스크립트 엔진에 의해 구현되어 있습니다. setTimeout은 비동기 함수로, 일정 시간이 지난 후 콜백 함수를 실행하는 기능을 제공합니다. 이 때 콜백 함수는 setTimeout 메서드에 전달된 시간이 경과한 후 이벤트 루프에서 실행되며, 이를 통해 비동기 작업의 완료 여부를 감지할 수 있습니다.

따라서 onload, then, setTimeout 등 모두 자바스크립트 언어 자체에 내장된 메서드이며, 해당 메서드를 사용하여 비동기 작업의 완료 여부를 감지할 수 있습니다.

Q: settimeout은 그러면 폴링하는 형식으로 동작하나?

setTimeout 메서드는 이벤트 루프의 특성을 이용하여 구현되어 있습니다. 일정 시간이 지난 후에 콜백 함수를 실행하도록 예약하는 기능을 제공합니다. 이때 콜백 함수는 특정 시간 간격마다 반복해서 실행되지 않습니다. 따라서 setTimeout 메서드는 폴링하는 형식으로 동작하지 않습니다.

setTimeout 메서드는 브라우저나 Node.js와 같은 환경에서 제공되는 기능으로, 내부적으로는 이벤트 루프의 태스크 큐와 타이머 기능을 이용하여 구현됩니다. 이 메서드를 호출하면, 일정 시간 이후에 실행될 콜백 함수가 타이머 큐에 등록됩니다. 그리고 이벤트 루프가 태스크 큐와 콜 스택을 순회하면서, 타이머 큐에 등록된 콜백 함수가 실행 대기 상태가 되었는지 주기적으로 확인합니다. 타이머 큐에 등록된 콜백 함수가 실행 대기 상태가 되면, 이벤트 루프는 해당 콜백 함수를 콜 스택에 추가하고 실행합니다.

결과적으로 setTimeout 메서드는 이벤트 루프와 타이머 기능을 이용하여, 특정 시간 이후에 콜백 함수를 실행할 수 있는 기능을 제공하는 비동기 함수입니다.

결론

비동기 작업들은 특정 함수나 키워드를 통해 이루어 지게 되는데, 이는 자바스크립트 내부에서 해당 작업 완료 여부를 알 수 있게 해주는 구현이 내부에 되어있다.

자바스크립트는 콜스택에 하나씩 함수가 쌓이고 실행된다.
이 과정에서 비동기 함수가 콜스택에 들어갈 경우, 해당 컨텍스트는 제거되고 콜백함수를 등록하게 된다.

이때 비동기 함수의 작업이 끝나면 콜백함수가 다시 콜스택에 쌓이게 되고 콜스택이 이를 실행함으로써

싱글 스레드이면서, 비동기적인 작업을 가능케 하는 것 이다.

콜스택

  • 있는거 하나씩 실행한다
  • 비동기면 제거하고 콜백 Queue에 넣어버린다.
  • 비어있으면 콜백 queue를 확인한다.

콜백 Queue

  • 비동기 함수가 끝나면 다시 콜스택에 넣는다.
profile
안녕하세요 반가워요!

0개의 댓글