Promise 의 동기 부분
다음 코드는 앞 게시글에서 한 번 비동기 코드는 영원한 비동기 코드 임을 예제로 든 코드입니다. 이번 글에서는 다음 예제를 Promise 를 이용해 비동기 코드인 setTimeout 안에서 선언된 a 를 꺼내보려고 합니다.
let a = 2;
setTimeout(() => {
let a = 5;
console.log(a);
}, 0);
const p = new Promise((resolve, reject) => {
setTimeout(() => {
a = 5;
console.log(a);
resolve(a); // 결괏값 a
}, 0);
});
Promise 안에 setTimeout 을 담습니다. 그러고서 resolve 해서 return 하고 싶은 결과값, a 를 넣습니다. 위와 같은 코드를 짤 수 있는 연습을 많이 해야합니다. Promise 는 다시 말해 실행은 바로 했지만 결과값을 자신이 원하는 때에 불러올 수 있는 것이라고 했습니다. 부르고 싶을 때는 다음 코드를 추가하면 되겠죠. 기존에 있던
p.then((result) => {
console.log('result', result);
});
추가로 직접 다른 코드를 썼는데, 코드의 위치는 중간에 있지만, 가장 먼저 출력되는 이유는 간단합니다. 해당 코드만 동기 코드이고, 나머지는 비동기 코드이기 때문이죠. 첫 줄에 5 는 console.log(a) 를 통해 나온 결과값이고, 두 번 째 결과값은 resolve(결과값) 에서 결과값으로 넣은 값이 a 이므로 5 가 result 5 나오게 됩니다.
Promise 의 동기부분
하지만, 짚고 가야할 부분이 있습니다. 프로미스에도 동기부분이 있다는 점이죠. 어디 부분인지 예제를 통해 바로 확인해보겠습니다.
정말 어렵습니다. 프로미스... 위 코드의 Promise 에서 동기부분은
(resolve, reject) => {
...
}
여깁니다. resolve, reject 를 포함한 함수가 모두 동기부분이라는 것이ㅛㅈ,,ㅣㅈㅁㄹ죠. 때문에 '다른코드' 보다 '여기가 동기부분' 이라는 결과값이 먼저 나올 수 있게 되는 겁니다. 나머지 '5' 와 'result 5' 는 Promise 의 resolve(a) 를 통해 result 값을 받아야 출력하는(then 의 익명함수를 실행하는) 특정 조건이 있기 때문에 비동기 코드가 맞습니다. 때문에 동기 코드인 둘 보다 결과값이 나중에 출력하게 되었습니다.
설명으로만 들어서 혼란스러우니 호출 스택을 통해 자세히 알아보겠습니다.
1. 우선, 가장 먼저 annoymous 가 호출되고, 이후에 쌓일 호출은 resolve 와 reject 가 있는 익명함수가 됩니다. 호출하는 코드가 전혀 보이진 않지만, 또 하나 새롭게 알아가는 건데 new Promise 는 다음에 올 함수를 호출하기 때문에 익명함수가 두 번째로 호출됩니다. 물론 동기 코드로써요. 때문에 console.log('여기가 동기 부분') 이 다음으로 호출되고, 콘솔에 제일 먼저 입력됩니다.
2. 그 다음으로 나온 setTimeout 은 동기 코드 안에 있어 호출이 되었지만, setTimeout 자체가 비동기 코드이므로 setTimeout 안에 있는 console.log(a) 와 resolve(a) 는 백그라운드에 들어가게 되면서 new Promise 로 인해 호출된 익명함수 (resolve, reject) => {} 는 종료됩니다.
3. 다음으로 있는 동기 함수인 console..log('다른 코드'); 가 호출되어 콘솔에 입력됩니다.
4. 마지막으로 p.then 으로 호출한 console.log('result', result) 가 비동기 코드로 백그라운드에 들어갑니다.
이렇게 동기함수만 실행된 채 호출 스택에 있는 함수(?)들이 모두 출력되었습니다.
다음으로 백그라운드에 있는 비동기 코드들을 이벤트루프로 호출스택에 보내보겠습니다.
1. 백그라운드에 들어온 순서대로 태스크 큐로 보냅니다. 이 때,
- console.log('result', result); 는 promise 이므로 micro queue,
- console.log(a), 0 과 resolve(a) 는 Macro queue 로
보냅니다. 하지만, consoel.log('result', result); 는 조건(Promise 의 익명함수에서 resolve(a) 를 통해 result 값을 받아야 함)이 충족되지 않아 백그라운드에 남아 있습니다.
2. 그리고 둘은 백그라운드에 들어온 시간 차로 순서가 명확하므로 Macro queue 에 있는 console.log(a), 0 / resolve(a) 가 들어간 setTimeout 의 익명함수 () => {} 부터 실행합니다. a = 5; 로 인해 a 의 값은 2 에서 5 로 다시 변수선언되고, 이를 콘솔에 출력합니다. 그리고 resolve(a) 로 인해 result 로 연결돼 promise 의 조건이 충족돼 그제서야 console.log('result', result); 는 micro queue 에 들어가게 됩니다. 또 setTimeout 의 익명함수는 종료되어 자연스레 호출스택은 비어 있게 되고, micro queue 에 있는 console.log('result', result); 가 포함된 p.then 의 익명함수 순서가 됩니다.
3. 마지막 순서로 p.then 의 익명함수인 console.log('result', result) 가 포함된 () => {} 가 실행되어 result 값으로 a 를 받은 result 는 5 가되고, 콘솔에 result 5 가 출력됩니다. 호출 스택, 백그라운드, 태스크 큐 모두 비어있게 되면서 코드는 종료됩니다.