방춘덕(고양이 키우면 지을 이름)의 개발 블로그입니다.
event loop 본문
JS는 단일 스레드 기반의 언어이며 이는 한 번에 한 작업밖에 처리하지 못한다는 것을 뜻한다. (이는 정확히 JS엔진에서만 국한된 것이다. 브라우저 등의 상황에서는 다르다) 하지만 우리가 흔히 접할 수 있는 웹 애플리케이션들은 HTTP 요청을 처리하며 화면에 애니메이션을 보여주고, 이에 대한 프리징이나 다른 현상들을 찾아볼 수가 없다. 이것에 대한 해답이 위의 사진에 있듯, event loop와 그 친구들이다.
1). call stack
브라우저 안의 자바스크립트 인터프리터와 같이, 인터프리터를 위한 메커니즘이다. 현재 실행되고 있는 함수는 무엇인지, 그 함수 내에서 어떤 함수가 호출되고 있는지, 다음에 어떤 함수가 호출되는지 등을 관리한다.
보통 다음과 같은 순서로 실행된다.
- 함수를 호출하면 인터프리터는 call stack에 함수를 추가 한 뒤, 함수를 실행한다.
- 실행 중인 함수에 의해 호출된 함수들은 모두 call stack에 추가되고 해당 호출된 위치에서 실행된다.
- 실행 중인 함수가 완료되면 인터프리터는 call stack에서 함수를 꺼내고, 이전 함수의 코드 실행이 중단된 위치 (종료된 함수가 호출된 위치)에서 실행을 재개한다.
- 더 이상 실행할 함수가 없을 때까지 반복한다.
- 하지만 도중에 call stack에 할당된 메모리보다 더 많은 메모리를 점유하려고 시도하는 경우 (재귀 함수 등)에는 stack overflow 에러가 발생할 수 있다.
이해가 어려울 수 있으니, 예제 코드를 한 가지 보면서 이해해보자.
function answer() {
return solve()
}
function solve() {
return 1 + 1
}
console.log('1 + 1은?', getAnswer()) // 1 + 1은? 2
이 코드는 아래와 같이 실행된다.
위의 4단계 대로 움직이는 것을 볼 수 있다.
이제 구조적으로 JS 엔진은 call stack에 의해 함수를 실행하므로 한 번에 하나의 작업밖에 실행하지 못한다는 것을 알았다. 이제 어떻게 API 호출 등을 해도 웹 애플리케이션이 멈추지 않는지 알아보자
2). web api
front-end 개발자가 웹 브라우저에서 사용할 수 있는 API를 말한다. BOM API (Browser Object Model API)라고도 하며 JS로 접근할 수 있지만, 구현은 브라우저가 개발된 언어로 되어있다. 예를 들어 chrome은 C++이다.
ajax, document, setTimeout 등.. 많은 web api들이 존재하며 그 대강의 목록은 여기서 볼 수 있다. 이 web api들은 JS 엔진이 아니라, 웹 브라우저 내부의 web api 실행 공간에서 따로 실행이 되기 때문에 웹 애플리케이션이 비 동기적으로 동작할 수 있게 해 준다.
3). callback queue
web api가 작업을 종료했다면, 그 결과를 callback queue에 보낸다. callback queue는 web api의 결과물이 JS 엔진의 call stack에 들어가기 전에 대기하는 곳이다.
web api의 결과가 나왔다면 , 바로 들어가면 될 것이지 왜 굳이 따로 기다리는 걸까? 그 이유는 web api의 결과를 스택의 아무 곳이나 구겨 넣을 수 있는 게 아니기 때문이다. call stack은 함수의 실행 정보, 실행 순서 등이 들어있고 만약, call stack의 아무 곳으로나 콜백이 들어가게 된다면 코드가 뒤죽박죽이 돼버릴 것이다.
console.log('시작')
setTimeout(function () {
console.log('난입해서 끝')
}, 0)
console.log('끝')
// 결과 : 시작 -> 끝 -> 난입해서 끝
따라서 setTimeout을 0으로 둔다고 해도, call stack이 비게 될 때까지 대기하기 때문에 결과가 위처럼 나오게 된다. 아래에 그 과정을 자세히 설명해 두었다.
4). event loop와 큰 흐름
console.log('시작')
setTimeout(function () {
console.log('난입해서 끝')
}, 0)
console.log('끝')
// 결과: 시작 -> 끝 -> 난입해서 끝
위의 코드를 하나씩 따라가 보자.
위에서 설명한 대로 setTimeout과 같은 web api함수들은 호출되면 JS 엔진 내부의 call stack이 아닌 브라우저에서 결과물을 리턴할 때까지 따로 실행되기 때문에 call stack과 web api가 따로 분리되었고, 따라서 다음으로 실행해야 할, console.log('끝')이 먼저 화면에 나타나게 된다.
setTimeout의 실행이 종료되고 callback queue로 옮겨진 다음, call stack이 빌 때까지 대기한다.
이후 call stack이 비었다면, callback queue에서 콜백을 가져와 실행시키고 종료된다.
즉, call stack이 모두 빌 때까지 대기한 후 callback queue에 있는 콜백들을 call stack에 옮기는 이 일련의 동작이 바로 event loop다.
이 글을 작성할 때 읽은 감사한 자료들.
https://www.youtube.com/watch?v=8aGhZQkoFbQ (강추, 이 영상이 이 글보다 더 유익할 수 있음)
https://developer.mozilla.org/ko/docs/Web/JavaScript/EventLoop
https://flaviocopes.com/javascript-event-loop/
https://meetup.toast.com/posts/89
https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4
https://codeburst.io/what-you-need-to-know-about-the-javascript-event-loop-ee3 fc32e59c1
https://stackoverflow.com/questions/49264524/where-do-the-browser-web-apis-run-in-javascript
'JS' 카테고리의 다른 글
prototype과 prototype chain (0) | 2019.12.16 |
---|---|
scope (1) | 2019.12.13 |
hoisting (0) | 2019.12.07 |
execution context의 생성과정 (0) | 2019.12.05 |
execution context 와 execution context stack (0) | 2019.12.02 |