[10분 테코톡] 🍗 피터의 이벤트루프(17분) 를 보고 정리한 글입니다 :)
console.log('하나'); // 1.
// 1초 뒤에 첫번째 인자로 들어간 콜백 함수를 실행한다.
setTimeout(function() {
console.log('셋'); // 3.
}, 1000);
// 첫번째 인자 : 특정 함수의 인자로 들어가는 함수 '콜백 함수'
// 두번째 인자 : 1000ms -> 1초(second)
console.log('둘'); // 2.
자바스크립트 엔진이 코드를 진행하다가 setTimeout과 같은 비동기 코드를 만나면, 이 코드를 자바스크립트의 뒷편에서 실행하게 됩니다.
'이벤트 루프'는 자바스크립트 코드가 실행되는 자바스크립트 엔진의 뒤편에서 일어나는 어떤 문맥의 일부로써 동작하는 하나의 장치라고 보시면 됩니다.
다른 함수의 인자로 넘겨지는 함수
콜백 수신 함수에 의해 특정 시점에 실행
동기 콜백의 경우 호출 즉시 실행
비동기 콜백의 경우 나중에 조건을 만족했을때 실행
이벤트 리스너, 타이머 / XMLHttpRequest 요청
// ☑️ 동기 콜백의 예제
function greeting(name) {
console.log( '안녕하세요' + name); // 4. 콘솔 로그 출력
}
function processUserInput(callback) {
const name = prompt('이름을 입력해주세요'); // 2. prompt로 이름을 입력받는다.
callback(name); // 3. 인자로 받은 콜백 함수를 호출한다
}
processUserInput(greeting); // 1. 함수에 함수를 인자로 넣어준다.
힙(Heap)은 그냥 간단히 '메모리 할당이 일어나는 부분' (변수나 객체들이 저장되는 창고)
호출 스택(Call Stack)은 '함수가 호출되는 순서대로 쌓이는 스택이다'라고 생각하면 됩니다.
함수 실행시 호출 스택에 해당 함수를 집어넣음
함수 return시(함수의 실행이 끝나면) 호출스택의 맨 위에 있는 해당 함수를 pop한다.
사실은 함수가 아니라 함수 실행 문맥(Execution Context)입니다.
'자바스크립트는 싱글 스레드 언어'
=> 호출 스택을 하나만 사용한다
=> 동시에 하나의 일만 처리할 수 있다.
🤔 그런데 함수가 실행되는 호출 스택이 하나뿐이면서 어떻게 비동기 요청을 지원하고 동시성에 대한 처리를 할 수 있을까요?
구조의 자세한 설명은 [JavaScript] 비동기 처리와 자바스크립트 엔진의 동작 에서 확인하실 수 있습니다 :) 이번 포스트에서는 '이벤트 루프'를 좀 더 자세히 알아보겠습니다.
setTimeout을 포함해서 DOM 메서드, HTTP 요청 같은 것들은 전부 자바스크립트 엔진의 바깥에 있는 Web API에서 제공하는 메서드들입니다.
Web API 메서드들은 작동을 마치면 전부 비동기 메서드들이기 떄문에 작동을 마치고 콜백함수를 콜백 큐에 집어넣습니다. 거기서 콜백 함수들이 실행을 대기하게 됩니다.
자바스크립트 엔진 자체는 싱글스레드이지만 실제로 자바스크립트가 구동되는 환경인 웹 브라우저 에는 여러개의 스레드가 사용됩니다. 조금 더 자세히 말하자면, Web API가 멀티스레드로 동작하는 것입니다. 그리고 자바스크립트 엔진이 이것들과 상호 연동을 하기 위해서 필요한 장치가 '콜백 큐'와 '이벤트 루프'다 라고 생각하면 됩니다.
document.querySelector('.btn').click(function() { // (A)
try {
$.getJSON('/이런저런 주소', function(res) { // (B)
// 여기서 에러 🚨
});
} catch (e) {
console.log('속보) 에러 발생: ' + e.message);
}
});
비동기 코드를 사용하다 보면 try-catch가 예상과는 다르게 에러를 잡지 못하는 경우가 발생합니다.
왜 그렇게 되는지 알아보도록 하겠습니다.
XMLHTTPRequest도 HTTP요청을 보내면서 사라지고, 콜백함수 B가 콜백 큐에 들어가게 됩니다. 그리고 역시나 호출 스택이 비어 있을때 B가 이벤트 루프에 의해서 호출스택으로 이동하게 됩니다. (B의 움직임 : Web API -> Callback Queue -> Call Stack)
❗️ 이 상황을 보면 B가 호출스택으로 옮겨지는 이 시점에서는 A가 이미 실행을 마치고 return을 한 상황입니다. 즉 A와 B는 완전히 다른 문맥 속에서 각각 동작을 하고 있습니다.
❗️그래서 A 내부에 있는 try-catch문이 B에서 발생하는 에러를 잡아내지 못합니다.
=> B를 감싸고 있던 try-catch 문을 B 내부로 넣어주시면 됩니다 🤭
document.querySelector('.btn').click(function() { // (A)
$.getJSON('/이런저런 주소', function(res) { // (B)
try {
// 여기서 에러 🚨
} catch (e) {
console.log('속보) 에러 발생: ' + e.message);
}
});
});