비동기 코드 작성의 어려움
자바스크립트로 비동기 코드를 작성하다보면 예상치 못 한 오류를 마주칠 때가 있다.
이번 포스팅에서 비동기 코드를 작성할 때 할 수 있는 실수들과 이를 방지하는 방법에 대해 알아보려고 한다.
이 글을 시작하기에 앞서 자바스크립트가 비동기를 처리할 때 이벤트 루프의 동작 방식에 대해 학습하는 것을 추천한다.
<브라우저 이벤트 루프>
https://growth-coder.tistory.com/308
자바스크립트 이벤트 루프 동작 원리 (브라우저)
이전 포스팅에서 node.js 동작 원리를 살펴보면서 이벤트 루프에 대해 알아보았다. https://growth-coder.tistory.com/305 [Node.js] Node.js 동작 원리 (node.js는 싱글 쓰레드일까? 멀티 쓰레드일까?)Node.js가 싱글
growth-coder.tistory.com
<node.js 이벤트 루프>
https://growth-coder.tistory.com/305
[Node.js] Node.js 동작 원리 (node.js는 싱글 쓰레드일까? 멀티 쓰레드일까?)
Node.js가 싱글 쓰레드인지 멀티 쓰레드인지 알기 위해 Node.js가 자바스크립트 코드를 어떠한 방식으로 실행하는지 알아야 한다. 자바스크립트는 기본적으로 브라우저에서 실행하기 위한 언어이
growth-coder.tistory.com
아래 포스팅은 이벤트 루프 관점에서 자바스크립트가 Promise의 then, catch 메소드를 처리하는 방식인데 굉장히 자세하게 설명되어 있어서 읽고 오는 것을 추천한다.
https://velog.io/@sehyunny/js-visualized-promise-execution
(번역) 자바스크립트 시각화하기 : 프로미스 실행
자바스크립트의 프로미스는 처음엔 다소 어렵게 느껴질 수 있지만, 내부에서 일어나는 일을 이해하면 훨씬 더 쉽게 다가갈 수 있습니다.
velog.io
우선 다음은 브라우저에서 자바스크립트 이벤트 루프의 기본 구조이다.
Promise
자바스크립트 비동기 처리에 사용되는 객체인 Promise에 대해서 알아보자.
Promise에는 3가지 상태가 존재한다.
- pending : 비동기 처리가 완료되지 않은 상태
- fulfilled : 비동기 처리가 완료된 상태
- rejected : 비동기 처리가 실패한 상태
다음과 같은 비동기 코드를 살펴보자.
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("hello");
}, 1000);
}).then((message) => {
console.log(message);
});
Promise 생성자가 호출되면서 Promise 객체가 pending 상태로 생성된다.
resolve, reject를 파라미터로 가지는 executor 함수가 call stack에 올라와서 실행된다.
setTimeout이 타이머에 등록되고 실행이 끝났기 때문에 call stack이 비워진다.
지금까지 다음 코드까지 실행되었다.
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("hello");
}, 1000);
})
Promise 생성자로 전달해 준 함수가 실행되었고 1초 뒤에 resolve를 호출하도록 타이머에 등록한다.
타이머에 등록하면서 Promise 생성자로 전달해 준 함수가 종료되었고 그 다음 then이 실행된다.
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("hello");
}, 1000);
}).then((message) => {
console.log(message);
});
then이 call stack에 올라와 실행되면서 then의 콜백 함수는 Promise 객체의 promise fulfill reactions에 등록된다.
참고로 catch의 콜백 함수는 Promise 객체의 promise reject reactions에 등록된다.
이제 1초가 지나서 setTimeout의 콜백 함수가 macro queue에 등록된다.
call stack이 비워져있고 Microtask Queue도 비워져있기 때문에 event loop가 Macro Queue에서 콜백 함수를 가져와서 call stack에 넣는다.
resolve가 호출되면서 다음과 같은 과정이 일어난다.
- state가 fulfilled로 변경된다.
- promise result에 resolve의 파라미터가 담긴다.
- promise fulfill reactions에 등록된 함수가 call stack으로 올라간다.
이제 call stack의 함수가 실행되면서 프로그램이 종료된다.
댓글