😎자바스크립트에는 이벤트루프가 있다.
이벤트 루프... 너무나도 어렵다.
라고 생각하기 쉽지만 추상적으로 이해하는 건 생각보다 어렵지않다!!
그만큼.. 인터넷에도 여러 추상적이게 설명해주는 명강의들이 존재한다.
하지만 영어와 친하지 않은 나같은 사람들을 위해
적극 추천하는 링크를 남기도록 하겠다.
https://ko.javascript.info/event-loop#ref-635
아주 좋은 설명이 담겨있습니다.
후반부의 프로그레시브 바 등 실제로 웹 환경에서 써먹기 좋은 내용도 담겨있지만
그 후반부분은 우선 이벤트루프에 대한 이해가 선행되지 않으면 봐도 뭔소린지 모르니까..
이벤트 루프에 대한 이해가 충분히 선행되어야만합니다.
https://chanmi-lee.github.io/articles/2020-06/JavaScript-Visualized-Event-Loop
gif덕에 이해를 쉽게 할 수 있어진다. 매우 추천
이 글로 먼저 이벤트 루프에 대한 감을 잡은 다음
저 위의 포스트를 읽어보면 아주 좋습니다.
https://www.youtube.com/watch?v=wRPcxR1M7Uc&list=PLcqDmjxt30RsGIPBBKX7xl05VuqJeCTFn&index=8
위 포스트들을 통해 어느정도 이벤트 루프에 대한 감이 쌓인 상태에서 보면 아주 좋습니다.
아무래도 사전지식이 조금 있는것을 전제로 강의를 진행하다보니
아예 실행컨텍스트, 이벤트루프, 태스크큐에 대한 이해가 없는 채로 들으면
이게 대체 뭔 소리야 싶을수도 있을 것 같음
😎이벤트 루프가 왜 필요한가요?
자바스크립트는 기본적으로 싱글스레드 언어입니다.
싱글 스레드 언어는 한번에 하나의 작업(하나의태스크)만 처리할 수 있다는 한계를 가지며
따라서 기본적으로 자바스크립트는 동기 / 블로킹 방식의 언어입니다.
그렇기 때문에 자바스크립트는 일반적으로 함수가 호출되면
호출된 함수에게 제어권을 넘겨주는 식으로 동작합니다.
(물론 나중에 제너레이터 함수와 같이 제어권을 서로 토스해주는 함수도 나오지만
이것도 결국은 지정된 부분까지만 코드를 실행하고 다시 호출자에게 제어권을 넘겨주는 식입니다.)
즉 함수가 호출되게 되면 호출된 함수에게 제어권을 넘겨주기때문에
호출한 함수의 실행은 멈추게 된다는 뜻입니다.
자바스크립트의 이런 특성은 쉽고 간편한 프로그래밍을 할 수 있는 원동력이 되지만
반대로 비동기처리를 할 수 없다는 한계를 가지게됩니다.
하지만 실제 프로그래밍을 통해 뭔가를 구현하다보면 비동기적인 실행 방식이 없으면
너무나도 비효율적인 경우가 많이 있습니다.
예컨대 3초뒤에 뭔가를 보여주고 싶어서 타이머를 설정해두었는데
타이머가 돌아가는 동안 다른 작업을 안하고 3초가 다 지날때까지 기다렸다가
뭔가를 보여주고 다시 코드를 시작한다면??
굉장히 답답한 사이트가 만들어질 것입니다.
또 다른 경우 서버에게 어떤 데이터를 받아와서 그 데이터를 화면에 보여주어야하는데
서버에 요청한 것에 대한 응답은 언제 올지 불명확합니다.
서버의 응답을 기다리는 동안 다른 작업을 아예 할 수 없다면 어떨까요?
굉장히 답답할것입니다.
그렇기 때문에 자바스크립트는 이 한계를 외부의 도움을 통해 극복합니다.
😎이벤트 루프의 동작 방식
이벤트루프는 자바스크립트가 비동기적인 실행을 하도록 도와주는 무언가 중 하나입니다.
저 위의 그림은 이벤트 루프가 어떤 원리로 돌아가게 되는지에 대한 정리입니다.
이 과정에서 콜스택에 대한 지식이 필요하니
콜스택(실행컨텍스트 스택)에 대한 지식이 없으신 분들은 뒤로가기를 누르고
콜스택부터 공부하도록합시다.
정확히는 저렇게 돌아간다고하지만 간략하게 요약해서 보면 이렇습니다.
1. 매크로태스크 큐에서 가장 오래된 태스크를 꺼내 실행합니다(예: 스크립트를 실행)
//가장 오래된 태스크를 꺼내는건 FIFO (First In First Out)이라고 볼수있습니다. 즉 큐라는거죠!
2. 모든 마이크로태스크를 실행합니다.
3. 이 작업은 마이크로태스크 큐가 빌 때까지 이어지고 태스크는 오래된 순서대로 처리됩니다.
4. 렌더링할 것이 있으면 처리합니다.
5. 매크로태스크 큐가 비어있으면 새로운 매크로태스크가 나타날 때까지 기다립니다.1번으로 돌아갑니다.
그냥 제식대로 더 간략하게 요약하면
1. 콜스택이 비었다면 마이크로태스크큐에서 태스크를 꺼내 실행한다.
2. 마이크로 태스크 큐까지 다 비었다면 매크로태스크 큐에 있는걸 꺼내 실행한다.
자 이것과 콜스택의 상태를 유의하면서 아래 예제 코드의 콘솔 출력 결과를 예상해봅시다.
참고로 전 맞췄음 ㅎㅎ;;
function oneMore() {
console.log('onemore');
}
function run() {
console.log('run run');
setTimeout(() => {
console.log('wow'), 0;
});
new Promise((resolve, reject) => {
resolve('hi');
}).then(console.log);
oneMore();
}
setTimeout(run, 1000);
근데 좀 논외의 얘기인데 이 코드에서 console.log를 호출하는 게 아니라
그냥 console.log만 해놓는게 정말 이해가 안갔습니다.
오타냈나? 싶었는데 저렇게 돌려야 제대로 돌아가더라구요..
아무래도 resolve안에있는 값을 인자로 넣어서 then에있는 콜백함수를 실행시키는건가? 싶은..
프로미스를 좀 더 공부해봐야겠어요
😎블록컨텍스트.. 선생님.. 이 예제는 대체 뭐죠?
https://www.zerocho.com/category/JavaScript/post/609778ad9f879900043a8728
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
});
}
위 블로그를 보다가 문득 저 예제에서 의문이 들었습니다.
공식 스펙과는 다르다고 이야기하시기에
블록컨텍스트가 공식스펙에 실존하는지는 의문이 있습니다만..
아니... 저렇게 되면..?
제가 생각하기에 예상 동작은 이랬습니다.
let은 블록스코프를 사용하고 for문은 블록이다.
따라서 저 블록스코프의 i는 소멸한다.
그렇기 때문에 setTimeout의 콜백함수인 익명화살표함수는
실행컨텍스트는 전역이랑 eval, 함수만 생성하니까...
전역에 있는 i를 검색해보고 call stack에 아무것도 남지않은 시점의
전역 i값을 쭈르륵 출력할거라고 생각했습니다.
만약에 i가 없다면.. 참조오류가 발생하거나요..
하지만 실제 동작은 0, 1, 2였습니다.
그럼 var나 선언없이 사용하면 어떨까요? 그럼 i는 for문이 블록이기때문에
전역에 선언이 될것이고
그럼 콜스택이 비워진 시점에서 i는 3이니까
3 , 3 , 3 이 출력됩니다.
그런데 의문은 대체 왜 let으로 선언한 경우에
0, 1, 2가 찍힐 수 있는가...
왜..?지..????
라고 생각하면서 블록 컨텍스트라는 개념이 존재한다고 회로를 돌리면서
생각해본 결과.. 그러면 말이 맞는다는 결론에 도달합니다.
😎저게.. 왜 맞는데?
블록 스코프에서는 변수와 스코프체인만 있다. this는 상위 this를 따라간다.
뭐 하여튼... 자기가 호출되었을 때의 환경을 기억하고 찾아가서 참조하는데
그 환경이 전역보다 가까이 있어서 그 환경을 참조하게되어서 0,1,2가 나오는구나...
대충은 이해되는데 확실히는 와닿지가 않네요
'javascript' 카테고리의 다른 글
프로미스를 회처럼 날로 먹는 방법 (3) | 2023.03.15 |
---|---|
타이머 함수를 케이크처럼 쉽게 이해하는 법 (2) | 2023.03.12 |
생존법칙1 reduce 메서드를 이해해라. (0) | 2023.03.09 |
이터러블과 이터레이터를 이해하는 방법 (0) | 2023.03.06 |
너무 재미있는 예제로 알아보는 실행컨텍스트 (0) | 2023.03.05 |