노션에 정리하며 공부했던 기록들입니다.
👉🏻 one thread == one call stack == one thing at a time
자바스크립트는 싱글 스레드 언어이기에 함수를 실행하면 함수 호출이 스택에 순차적으로 쌓이고, 스택의 맨 위에서부터 차례대로 한 번에 하나의 함수만 처리할 수 있다.
간단한 프로그램이라면 상관없지만 만약에 우리가 아주 복잡한 프로그램을 구동한다고 생각했을때, 시간이 매우 오래 걸리는 작업이 스택에 쌓이고 실행되면 그 다음 작업은무한정 대기할 수밖에 없다.
이처럼 다른 작업을 실행하기 위해서 이 전 작업이 완료될 때까지 기다려야만 하는 상황을 블로킹(blocking)이라고 한다.
극복하기 위한 해결 방안이 바로 Asynchronous Callbacks(비동기 콜백)이다
자바스크립트가 싱글 스레드 언어임에도 불구하고 웹 사이트에서 끊김없이 여러 작업을 동시에 할 수 있는 것은 바로 브라우저가 Web APIs 같은 것들을 제공하여 비동기 작업을 가능하게 해주기 때문이다.
함수를 동기 호출하게 되면 call stack에 차곡차곡 쌓여 순차적으로 실행된다.
만약 AJAX | setTimeout | DOM event 함수를 실행하면, 자바스크립트 엔진은 call stack에서 Web APIs로 보내고 정해진 시간 혹은 이벤트가 발생한 순간에 순차적으로 callback queue에 적재한다.
callback queue에 줄을 선 함수들은 call stack에 쌓여있던 것들이 모두 제거되어 깨끗해지면 차례대로 스택에 쌓여서 실행되게 된다.
function main(){
console.log(1);
setTimeout(function cb() {
console.log(2);
}, 5000};
console.log(3);
}
//1 3 2
먼저 main() 함수가 실행되고, console.log(1)이 스택에 쌓인다.
console.log(1)이 실행되어 콘솔창에 1이 출력되고 setTimeout의 콜백함수 cb가 스택에 쌓이는데, setTimeout은 브라우저에 의해 제공된 API로 자바스크립트 엔진에서 처리하지 않고 바로 web APIs로 넘긴다.
그러면 브라우저는 마치 setTimeout 함수가 완료된 것처럼스택에서 pop하고 다음 작업을 진행하므로 console.log(3)이 실행되어 콘솔 창에 3이 출력된다.
모든 코드가 실행되었으므로 main() 함수가 스택에서 제거되고, 5초 동안 대기하고 있던 cb 함수가 5초가 지난 시점에 task queue에 들어온다.
stack이 비어있으므로 cb 함수를 stack에 적재하고 console.log(2)를 실행하게 된다.
이 경우에도 결과는 크게 다르지 않다.
task queue에 줄 서 있는 callback 함수들은 stack이 비어있을 때만 stack으로 이동할 수 있기 때문이다.
위 경우 setTimeout으로 설정한 cb 함수는 web APIs로 이동하는 즉시 task queue로 이동하게 되는데, stack이 비어있지 않기 때문에 대기 상태로 있게 되고 console.log(3)이 출력되고 스택이 클리어되면 이동하여 console.log(2)가 실행된다.
👉🏻 모든 web APIs는 위와 같은 방식으로 작동된다. AJAX나 DOM 이벤트도 동일하다.document.querySelector('button').addEventListener('click', function () {
console.log('clicked');
});
브라우저는 일단 click event 함수를 call stack에 저장하고 이는 즉시 web APIs로 옮겨진다.
그 상태로 무한 대기하고 있다가 사용자가 버튼을 클릭하는 순간 click 이벤트의 콜백 함수는 callback queue로 이동한다. 그리고 stack이 비는 순간에 stack으로 이동하여 함수를 실행한다.
만약에 사용자가 버튼을 10번 누른다면 callback queue에 10개의 콜백 함수가 쌓일 것이고먼저 들어온 콜백부터 순차적으로 스택으로 이동하여 실행되고 없어지고를 반복할 것이다.
setTimeout(function timeout () {
console.log(1);
}, 1000);
setTimeout()을 4번 썼다고 생각하면
이 경우 각각의 함수들은 stack -> web APIs로 이동하고 차례대로 callback queue에 쌓이게 된다.
이 때 가장 먼저 적재된 timeout 함수부터 순차적으로 stack에 "이동 -> 실행 -> 제거"를 반복하게 된다.
즉, 4개의 함수를 1초 후에 실행하라고 설정했다고 해서 모든 이벤트가 동시에 실행되는 것이 아니다.
이 점을 항상 유의해야한다.
AJAX나 setTimeout() 등을 이용하면 마치 자바스크립트가 여러 가지 일을 동시에 수행하는 것처럼 보이지만 그것은 일종의 눈속임일 뿐이고 자바스크립트는 오직 한 번에 하나의 작업만을 수행한다.그렇기 때문에 위의 timeout 이벤트는 queue에 줄 서서 하나씩 차례대로 스택으로 이동하여 실행된다.
그러므로 setTimeout()과 같은 메소드로 어떤 시간을 설정한다고 해서 아주 정밀하고 정확한 시간이 보장되는 것은 아니다.
모두 미세한 오차가 존재하며 브라우저는 단지 그 오차를 최소한으로 줄여줄 뿐이다.
window.addEventListener('scroll', function () {
console.log('hello');
});
스크롤 이벤트를 발생시키는 함수의 경우, 스크롤 이벤트는 약간의 움직임에도 많은 이벤트가 실행된다.
이 경우 브라우저는 아주 많은 콜백을 callback queue에 적재하게 된다.
만약 위와 같이 아주 간단한 코드 한 줄이라면 상관없겠지만 매우 복잡한 이벤트가 일어나야한다면 프로그램 성능에 좋지 않은 영향을 줄 것이다.
그래서 디바운싱(debouncing)을 통해 이벤트가 큐에 적재되는 속도를느리게 만든다거나 하는 방법을 사용할 수 있다.