동기와 비동기
동기는 한가지 작업의 완료 시점과 다른 작업의 시작 시점이 같은 동작 방식을 말한다. 현재 실행 중인 태스크가 종료할 때까지 다음에 실행될 태스크가 대기하는 방식을 동기 처리라고한다.
비동기는 한가지 작업이 끝나는 시점과 다른 작업이 시작하는 시점 관계없이 다음 동작을 실행할 수 있는 방식. 현재 실행 중인 태스크가 종료되지 않은 상태라 해도 다음 태스크를 곧바로 실행하는 방식을 비동기 처리라고 한다.
blocking & non-blocking
blocking
은 하나의 작업이 끝날 때까지, 이어지는 작업을 "막는 것"이다.non-blocking
은 하나의 작업이 끝나지 않아도, 이어지는 작업을 하는 것이다.
Javascript는?
Javascript는 synchronous, blocking, single-thread 언어 but 비동기 처리가 가능한 언어이다.
자바스크립트는 단일 호출 스택을 가진 single-thread 프로그래밍 언어이기 때문에 한번에 하나의 작업만 처리할 수 있다. 즉, 자바스크립트는 동기적 언어이다. 동기적인 방식은 서버로 데이터를 요청하거나 큰 용량의 파일을 다운 받는 동안 다른 작업을 할 수 없기 때문에 부정적인 사용자 경험을 제공한다. 이러한 이유로 비동기적 작동은 웹 개발에 필요하다. 우리는 아래 내용을 통해 자바스크립트를 비동기적으로 동작하도록 조작할 수 있다.
자바스크립트의 비동기 처리 with Web API
자바스크립트는 동기적 언어지만 브라우저가 동작하는 것을 보면 많은 태스크가 동시에 처리되는 것처럼 느껴진다. HTML 문서를 보여주고, HTTP 요청을 통해 서버로부터 데이터를 가지고 오면서 렌더링도 한다. 이렇게 자바스크립트의 동시성을 지원하는 것은 이벤트 루프다. 자바스크립트 엔진은 단순히 태스크가 요청되면 호출 스택을 통해 태스크를 순차적으로 실행할 뿐이다. 비동기 처리에서 소스코드의 평가와 실행을 제외한 모든 처리는 자바스크립트 엔진을 구동하는 환경인 브라우저 또는 Node.js가 담당한다. 이를 위해 브라우저 환경은 태스크 큐와 이벤트 루프를 제공한다.
- 자바스크립트의 호출 스택에 비동기 함수의 호출이 있을 경우 해당 함수는 Web API(혹은 백그라운드)로 이동된 후 호출 스택에서 제거한 후 자바스크립트는 다음 작업을 진행한다.
- Web API(혹은 백그라운드)로 이동된 함수는 처리 완료되는 순서로(이벤트면 해당 이벤트가 발생하거나, setTimeout의 타임아웃을 기다리거나, 서버로부터 데이터를 받아오는 등) 해당 함수의 콜백 함수가 태스크 큐에 푸쉬된다. => 이동된 함수의 처리는 브라우저가 실행한다.
- 이벤트 루프는 호출 스택이 비어 있고 태스크 큐에 대기 중인 함수가 있다면 순차적으로 태스크 큐에 있는 함수를 호출 스택으로 이동시킨다.
- 호출 스택으로 이동된 함수는 실행된다. 즉, 태스크 큐에 보관된 함수들은 비동기 처리 방식으로 동작한다.
자바스크립트 엔진은 싱글 스레드로 동작하지만 자바스크립트가 구동되는 환경(브라우저, Node.js)은 멀티 스레드로 동작한다.
예시
DOM element 이벤트 핸들러
- 마우스, 키보드 입력
- 페이지로딩 (DOMContentLoaded 등)
$.on('button', 'click', function onClick() {
console.log('You clicked the button!');
});
console.log('Hi!');
setTimeout(function timeout() {
console.log('Click the button!');
}, 5000);
console.log('Welcome to loupe.');
// 'Hi!'
// 'Welcome to loupe.'
// 5초 후
// 'Click the button!'
// 마우스 클릭하면
// 'You clicked the button!'
타이머
- 타이머 API (setTimeout 등)
- 애니메이션 API (requestAnimationFrame)
// #1
console.log('Hello');
// #2
setTimeout(function() {
console.log('Bye');
}, 3000);
// #3
console.log('Hello Again');
// 'Hello'
// 'Hello Again'
// 3초 뒤에
// 'Bye'
서버에 자원 요청 및 응답
- fetch API
- AJAX (XHR)
function getData() {
var data;
$.get('https://domain.com/products/1', function (response) {
data = response;
});
return data
}
console.log(getData()); // undefined
자바스크립트의 비동기 처리 with Promise
Web API의 비동기 함수를 사용한 것과 마찬가지로 Promise를 사용하면 비동기 처리할 수 있다.
- 자바스크립트의 호출 스택에 프로미스의 후속처리 메서드가 있을 경우 해당 메서드의 콜백 함수는 태스크 큐가 아닌 마이크로태스크 큐에 저장된 후 호출 스택에서 제거되고 자바스크립트는 다음 작업을 진행한다.
- 이벤트 루프는 호출 스택이 비어 있고 마이크로태스크 큐에 대기 중인 함수가 있다면 순차적으로 대기 중인 함수를 호출 스택으로 이동시킨다. => 이 때, 마이크로태스크 큐와 태스크 큐 모두 대기 중인 함수가 있다면 마이크로태스크 큐에서 대기하고 있는 함수를 먼저 가져와 실행한다. 즉, 마이크로태스크 큐는 태스크 큐보다 우선순위가 높다.
- 호출 스택으로 이동된 함수는 실행된다.
// 이행한 프로미스를 받으면 'then' 블록도 바로 실행되지만,
// 핸들러는 아래 console.log에서와 같이 비동기적으로 실행됨
const resolvedProm = Promise.resolve(33);
let thenProm = resolvedProm.then(value => {
console.log("이 부분은 호출 스택 이후에 실행됩니다. 전달받은 값이자 반환값은 " + value + "입니다.");
return value;
});
// thenProm의 값을 즉시 기록
console.log(thenProm);
// setTimeout으로 함수 실행을 호출 스택이 빌 때까지 미룰 수 있음
setTimeout(() => {
console.log(thenProm);
});
// 로그 출력 결과 (순서대로):
// Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
// "이 부분은 호출 스택 이후에 실행됩니다. 전달받은 값이자 반환값은 33입니다."
// Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 33}
비동기 처리의 문제점과 해결 방안
비동기 처리는 보다 좋은 사용자 경험을 제공하지만, 여러 비동기 동작들의 동작 시점을 예측할 수 없다는 문제가 있다. 이 문제를 해결하기 위해 다음 3가지 방법을 사용할 수 있다.
- Callback
- Promise
- Async/await
참고
- https://velog.io/@xortm854/JavaScript-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%98%88%EC%8B%9C%EB%A1%9C-Event-Loop%EB%A5%BC-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90
- https://0e.medium.com/%E1%84%83%E1%85%A9%E1%84%83%E1%85%A2%E1%84%8E%E1%85%A6-promise%E1%84%80%E1%85%A1-%E1%84%86%E1%85%AF%E1%86%AB%E1%84%83%E1%85%A6-b0940365610d
'프로그래밍 공부 > Javascript' 카테고리의 다른 글
[자바스크립트] 이벤트 리스너 사용 시 this 바인딩 문제 (0) | 2021.11.19 |
---|---|
[JS/Node.js] Callback, Promise, async/await / TIL 28일차 (0) | 2021.10.16 |
객체 지향 프로그래밍: 클래스와 프로토타입 / TIL 21일차 (0) | 2021.10.05 |
submit 이벤트와 preventDefault (0) | 2021.10.03 |
HTMLCollection 과 NodeList (0) | 2021.10.03 |