Asynchronous (Web API - setTimeout, setInterval, XMLHttpRequest, Callback Queue)
Web API
JavaScript는 싱글 쓰레드 기반의 프로그래밍 언어로 동기적으로 작동함
- 싱글쓰레드: 한 번에 하나의 태스크만을 실행할 수 있는 방식
- 멀티쓰레드: 한 번에 여러개의 태스크를 실행할 수 있는 방식
따라서 네트워크 요청, 타이머, 애니메이션 등 오래 걸리고 반복적인 작업들은 자바스크립트 엔진이 아닌 브라우저 내부의 멀티 스레드인 Web APIs에서 비동기 + 논블로킹으로 처리됨(비동기 + 논블로킹은 메인 스레드가 작업을 다른 곳에 요청하여 대신 실행하고, 그 작업이 완료되면 이벤트나 콜백 함수를 받아 결과를 실행하는 방식)
▶ 이벤트 루프 동작 과정
싱글 스레드인 자바스크립트의 작업을 미리 스레드로 돌려 작업을 동시에 처리하거나 여러 작업 중 어떤 작업을 우선으로 동작시킬것인지 결정하는 세심한 컨트롤을 하기 위해 존재하는 것이 바로 이벤트 루프(Event Loop)
이벤트 루프는 브라우저 내부의 Call Stack, Callback Queue, Web APIs등의 요소들을 모니터링하면서 비동기적으로 실행되는 작업들을 관리하고, 이를 순서대로 처리하여 프로그램의 실행 흐름을 제어(간단히 말하면 브라우저의 동작 타이밍을 제어하는 관리자라고 보면 됨)
이벤트 루프의 동작 과정을 간단히 살펴보자면, 자바스크립트의 setTimeout이나 fetch 와 같은 비동기 자바스크립트 코드를 브라우저 Web APIs에게 맡기고, 백그라운드 작업이 끝난 결과를 콜백 함수 형태로 큐(Callback Queue)에 넣고 처리 준비가 되면 호출 스택(Call Stack)에 넣어 마무리 작업을 진행
이러한 이벤트 루프를 이용한 프로그램 방식을 이벤트 기반(Event Driven) 프로그래밍이라고 함
이벤트 기반 프로그래밍은 프로그램의 흐름이 이벤트에 의해 결정되는 방식
예를 들어 키보드 입력과 같은 이벤트가 발생하면, 맞는 콜백 함수가 실행됨(ex.addEventListener(이벤트명, 콜백함수))
이벤트 기반 프로그래밍은 비동기 작업을 쉽게 처리할 수 있고, 멀티 스레드 언어에 비해 단순하고 직관적인 코드 작성을 가능하게 하며, 브라우저와 같은 환경에서도 안정적인 실행을 가능하게 하여 사용자와의 상호작용을 높일 수 있음
▶ 자바스크립트 엔진 구동 환경
자바스크립트를 실행하는 소프트웨어로는 우리가 잘 알고 있는 웹브라우저와 런타임(해당 프로그래밍 언어로 작성된 코드가 구동되는 환경)인 Node.js가 있음
브라우저의 내부 구성도
- Call Stack: 자바스크립트 엔진이 코드 실행을 위해 사용하는 메모리 구조
- Heap: 동적으로 생성된 자바스크립트 객체가 저장되는 공간
- Web APIs: 브라우저에서 제공하는 API 모음으로, 비동기적으로 실행되는 작업들을 전담하여 처리(AJAX 호출, 타이머 함수, DOM 조작 등)
- Callback Queue: 비동기적 작업이 완료되면 실행되는 함수들이 대기하는 공간
- Event Loop: 비동기 함수들을 적절한 시점에 실행시키는 관리자
- Event Table: 특정 이벤트(timeout, click, mouse 등)가 발생했을 때 어떤 callback 함수가 호출되야 하는지를 알고 있는 자료구조(위 그림에는 없음)
Web APIs의 종류
Web APIs는 타이머, 네트워크 요청, 파일 입출력, 이벤트 처리 등 브라우저에서 제공하는 다양한 API를 포괄하는 총칭
Web API는 브라우저(Chrome)에서 멀티 스레드로 구현되어 있음
그래서 브라우저는 비동기 작업에 대해 메인 스레드를 차단하지 않고 다른 스레드를 사용하여 동시에 처리할 수 있음
예를 들어, setTimeout 비동기 작업은 Web APIs의 한 종류인 Timer API 에서 타이머 스레드를 사용하여 타이머를 수행되며 XMLHttpRequest , fetch와 같은 네트워크 관련 API는 네트워크 스레드를 사용하여 네트워크 요청과 응답을 처리됨
Web APIs의 대표적인 종류
- DOM : HTML 문서의 구조와 내용을 표현하고 조작할 수 있는 객체
- XMLHttpRequest: 서버와 비동기적으로 데이터를 교환할 수 있는 객체. AJAX기술의 핵심.
- Timer API: 일정한 시간 간격으로 함수를 실행하거나 지연시키는 메소드들을 제공
- Console API : 개발자 도구에서 콘솔 기능을 제공
- Canvas API: <canvas> 요소를 통해 그래픽을 그리거나 애니메이션을 만들 수 있는 메소드들을 제공
- Geolocation API: 웹 브라우저에서 사용자의 현재 위치 정보를 얻을 수 있는 메소드들을 제공
이때 조심해야 하는 점이 모든 Web API들이 비동기로 동작되는 것이 아니라는 것
Web API에는 동기적으로 처리되는 것과 비동기적으로 처리되는 것이 모두 있음
예를 들어 DOM API나 Console API는 동기적으로 처리되고, XMLHttpRequest나 Timer API는 비동기적으로 처리됨
콜백큐(Callback Queue)의 종류
콜백큐 도 여러가지 종류의 Queue를 묶어 총칭하는 개념
콜백큐 에는 (macro)task queue와 microtask queue 두 가지 종류가 있음
- 테스크 큐=매크로테스크 큐(Task Queue) setTimeout, setInterval, fetch, addEventListener와 같이 비동기로 처리되는 함수들의 콜백 함수가 들어가는 큐(macrotask queue는 보통 task queue라고 부름)
- 마이크로테스크 큐(Microtask Queue): promise.then, process.nextTick, MutationObserver와 같이 우선적으로 비동기로 처리되는 함수들의 콜백 함수가 들어가는 큐
- 그 어떤 곳보다 가장 먼저 우선으로 콜백이 처리됨(브라우저 화면을 렌더링하는 과정보다 더 먼저 처리)
- 잘못된 무한 루프 동작의 버그의 코드가 들어가면 웹 페이지가 먹통이 되어버리는 현상 발생(Mincrotask Queue가 비어있지 않으므로 다른 테스크 큐나 이벤트 콜백이 실행될 수 없기 때)
또한 같은 queue 안에 적재되는 콜백이라도 어떠한 비동기 작업이냐에 따라 우선순위가 다른 태스크들이 있을 수 있음
예를들어 Microtask Queue에 적재되는 Promise 와 Mutation ObserverVisit Website 콜백 중 Mutation Observer이 먼저 처리됨
AnimationFrame Queue
브라우저의 큐는 브라우저 애니메이션 작업에 대한 처리를 담당하는 AnimationFrame Queue도 있음
자바스크립트 애니메이션 동작을 제어하는 requestAnimationFrame 메소드를 통해 콜백을 등록하면, 이 큐에 적재되어 브라우저가 repaint 직전에 AnimationFrame Queue에 있는 작업들을 전부 처리
따라서 자바스크립트 스타일 관련 코드들을 AnimationFrame Queue에 비동기로 처리하도록 구성하면 브라우저가 애니메이션의 타이밍을 관리하고, 적절한 프레임 속도를 유지하고, 다른 탭이나 창에 있을 때 애니메이션을 중지함으로써 브라우저의 애니메이션 동작의 성능과 품질을 향상시킬 수 있음
Animation Frame의 우선순위는 브라우저에 따라 다를 수 있음
테스크 큐와 마이크로 테스크 큐가 모두 처리된 후에 실행될 수 있고, 그 사이에 실행될 수도 있음
이벤트 루프의 명세가 업데이트 렌더링 단계에서 언제 콜백을 호출할지 정확하게 정의하지 않았기 때
▶ 자바스크립트 이벤트 루프 동작 과정
이벤트 루프(Event Loop)는 비동기 함수 작업을 Web API에 옮기는 역할을 하고 작업이 완료되면 콜백을 큐(Queue)에 적재했대가 다시 자바스크립트 엔진에 적재해 수행시키는 일종의 작업을 옮기는 역할만 함
작업을 처리하는 주체는 자바스크립트 엔진과 Web API
이벤트 루프는 Call Stack에 현재 실행 중인 작업이 있는지 그리고 Task Queue에 대기 중인 작업이 있는지 반복적으로 확인하는 일종의 무한 루프만을 돌고, 대기 작업이 있다면 작업을 옮겨주는 형태로 동작한다고 보면 됨
웹브라우저와 Node.js의 Web API 차이
웹브라우저의 Web APIs 와 Node.js 의 Node.js APIs 들은 구성은 비슷하지만 동작 측면에서 약간 차이가 있음
웹브라우저의 Web APIs는 비동기 작업이 끝나면 스스로 callback queue에 적재하지만, Node.js API들은 이벤트 루프가 직접 옮겨줌예를들어 Timer Web API에서 타이머가 모두 지나가면, 자바스크립트 환경이 웹브라우저냐 Node.js 냐에 따라 차이가 갈림
- Node.js : Timer API가 타이머 완료 이벤트를 발생시키고, 이벤트 루프가 이를 감지하여 Task Queue에 콜백 함수를 추가
- 웹브라우저 : Timer API가 스스로 Task Queue에 콜백 함수를 추가
setTimeout
setTimeout()은 특정 시간이 지난 다음에 코드를 실행하는 함수
자바스크립트 코드에 일종의 타이머를 설정하는 것
setTimeout(functionRef, delay, param1, param2, /* … ,*/ paramN)
매개변수로
- functionRef: 타이머가 만료된 뒤 실행할 function
- delay(Optional): 주어진 함수를 실행하기 전에 기다릴 밀리초 단위 시간(생략하거나 0을 지정할 경우 즉시 - 다음 이벤트 사이클에 실행하다는 점)
- param1, ..., paramN(Optional): functionRef에 전달할 추가 매개변수(함수에 바로 매겨변수를 전달하게 되면 자바스크립트는 기다림 없이 function을 실행함 - 첫번째 매개 변수로 함수참조가 아닌 함수 호출을 전달했기 때문 )
setTimeout(function(){
console.log("Hello World");
}, 2000);
console.log("setTimeout() example...");
// setTimeout() example...
// Hello World
function greeting(name, role){
console.log(`Hello, my name is ${name}`);
console.log(`I'm a ${role}`);
}
setTimeout(greeting, 3000, "So", "Software developer");
// 3초후
// Hello, my name is So
// Software developer
setTimeout 함수 취소
clearTimeout()을 사용하면 setTimeout()의 함수 실행을 취소할 수 있음
이를 위해 clearTimeout() 함수는 setTimeout()으로부터 반환된 id가 필요함
clearTimeout(id);
const timeoutId = setTimeout(function(){
console.log("Hello World");
}, 2000);
clearTimeout(timeoutId);
console.log(`Timeout ID ${timeoutId} has been cleared`);
// Timeout ID 24 has been cleared
만약 여러 개의 setTimeout() 함수들을 사용할 경우, 각 함수 호출에서 반환되는 ID 값들을 저장한 다음, 함수의 수 만큼 clearTimeout() 함수를 호출해야 함
setInterval
각 호출 사이에 고정된 시간 지연으로 함수를 반복적으로 호출하거나 코드 스니펫(코드 조각)을 실행
즉, 어떠한 코드를 일정 시간 간격을 두고 반복적으로 실행할 때 사용하는 함수
간격(interval)을 고유하게 식별할 수 있는 interval ID(인터벌 아이디)를 반환
interval ID는 setInterval() 함수를 호출할 때 마다 내부적으로 생성되는 타이머 객체를 가리키고 있음
setInterval(func, delay, arg0, arg1, /* … ,*/ argN)
매개변수로
- func: delay(밀리초)마다 실행되는 function. 첫 번째 실행은 delay(밀리초) 후에 발
- delay(Optional): 타이머가 지정된 함수 또는 코드 실행 사이에 지연해야 하는 밀리초(1/1000초) 단위의 시간입니다. 지정하지 않으면 기본 값은 0
- param1, ..., paramN(Optional): 타이머가 만료되면 func 에서 지정한 함수로 전달되는 추가 인수
setInterval(() => console.log(new Date()), 2000);
setInterval(() => console.log(new Date()), 1000);
// 2024-06-07T04:42:10.557Z
// 2024-06-07T04:42:11.571Z
// 2024-06-07T04:42:12.583Z
// ...
setInterval 함수 취소
clearInterval()을 사용하면 setInterval()의 함수 실행을 취소할 수 있음
이를 위해 clearTimeout() 함수는 setTimeout()으로부터 반환된 id가 필요함
const intervalId = setInterval(() => console.log(new Date()), 1000);
// 2024-06-07T04:43:52.417Z
// 2024-06-07T04:43:53.424Z
clearInterval(intervalId);
setTimeout()함수와 setInterval() 함수를 사용한 후에는 반드시 clearTimeout()함수와 clearInterval() 함수를 사용해서 타이머를 청소해주는 것이 좋음. 생성한 타이머는 눈에 보이지 않지만, 메모리 어디간에 계속 존재하고 있기 때문
그래서 생성만 하는 경우 해당 프로그램이 계속 반복 사용이 되면, 메모리 안에 타이머가 수천, 수만개게 남게 되면서 에러를 발생시킬 수 있음
XMLHttpRequest
XMLHttpRequest란 서버와 통신을 하도록 하는 객체
자바스크립트를 사용하여 HTTP 요청을 전송하려면 XMLHttpRequest 객체를 사용해야함
Web API인 XMLHttpRequest 객체는 HTTP 요청 전송과 HTTP 응답 수신을 위한 다양한 메서드와 프로퍼티를 제
- 비동기 통신: 페이지를 새로 고침하지 않고도 서버와 데이터를 주고받을 수 있음
- 다양한 데이터 형식 지원: XML, JSON, HTML, 텍스트 등 다양한 데이터 형식을 지원
- 상태 관리: 요청의 상태를 확인하고, 요청이 완료되었을 때 특정 작업을 수행
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data", true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
};
xhr.send();
XMLHttpRequest는 AJAX(Asynchronous JavaScript and XML)의 핵심 요소로 동적인 웹 애플리케이션 개발에 필수적이었지만 최근에는 더 현대적이고 사용하기 쉬운 fetch API가 많이 사용됨
fetch는 Promise 기반의 인터페이스를 제공하여 비동기 작업을 더 간단하게 처리할 수 있게 함
fetch 사용 예
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
참고
자바스크립트 set Timeout()로 타이머 설정하기