
export default function TastQueuePage(){
const onClickTimer = () => {
console.log("=======시작~~=======")
setTimeout(() => {
console.log("0초 뒤에 실행된답니다 😎️")
}, 0)
console.log("=======끝~~=======")
}
return <button onClick={onClickTimer}>시작</button>;
}
console.log("0초뒤에 실행된답니다") 가 가장 마지막에 실행됨
자바스크립트에서 실행되는 함수들은 Callstack 에 쌓이게 됨
이 때 setTimeout 같은 일정 시간이 걸리는 함수들은 해당 함수가 실행 후 종료되기까지를 기다리지 않고 Taskqueue 로 넘겨버림 (비동기 처리)
이후 Callstack의 모든 함수들을 처리한 이후 Taskqueue로 넘어간 함수들이 Callstack으로 넘어가서 처리 됨
+a) 싱글 스레드
+a) 멀티 스레드
여러 개의 스레드가 여러가지 작업을 번갈아가면서 빠르게 수행
비동기처리를 하지않고, 하나의 요청에 대한 응답을 기다렸다가 모두 처리된 이후 다음 작업으로 이동 (Context switching)
멀티 스레드 = blocking 방식 (하나의 요청에 대한 응답이 와야지만 다음 작업을 수행가능)
function aaa(qqq){
// 외부 API에 데이터 요청하는 로직
// ...
// ...
// 요청 끝!
const result = "요청으로 받아온 데이터 결과값"
qqq(result) // 요청 끝나면 qqq 실행시키기
}
qqq(result) => {
console.log("요청이 끝났습니다.")
console.log("요청으로 받아온 데이터는" + result + "입니다")
}
Callback 함수 = 함수의 인자로 들어가는 함수
위와 같이 특정 함수의 실행이 끝나고 해당 실행결과를 Callback 함수에 인자로 넣어서 Callback 함수를 실행하는 방식으로 사용
*이런 방식으로 함수의 실행 결과를 기다리지 않고 이를 바로 Callback 함수에 인자로 넣어서 결과를 도출하여 비동기 처리
Callback 실습

XMLHttpRequset (JS 에 내장되어 있는 기능)
- 서버와 상호작용하기 위해 사용되는 객체
- 페이지의 새로고침 없이 URL로부터 data를 받아올 수 있음
새로운 XMLHttpRequest 를 생성하고, .open 으로 POST 방식과 URL 입력
.send 로 해당 URL 로 요청을 보냄
.addEventListener 를 이용해 요청결과가 load 되면 지정한 함수를 실행
위 과정을 반복하면서 받아온 결과값을 다시 URL의 값으로 지정하여 요청을 보내고, 이에 대한 응답결과를 다시 URL의 값으로 지정해서 새로운 요청을 보냄
*이 과정을 통해 각 함수의 요청에 대한 응답을 기다리지 않고 바로 다음으로 실행할 함수에서 또 다른 요청의 값으로 활용함으로써 비동기 처리 가능
[BUT!] => 요청이 늘어날 수록 코드 형태가 대각선으로 점점 파이면서 가독성이 심각하게 떨어지게 됨! (콜백지옥)
Promise 실습
const onClickPromise = () => {
axios
.get("http://numbersapi.com/random?min=1&max=200")
.then((res) => {
const num = res.data.split(" ")[0];
return axios.get(`https://koreanjson.com/posts/${num}`);
})
.then((res) => {
const userId = res.data.UserId;
// prettier-ignore
return axios.get(`https://koreanjson.com/posts?userId=${userId}`)
})
.then((res) => {
console.log(res.data);
});
};
앞서 Callback의 문제를 해결하기 위해 Promise 를 사용할 수 있음
Promise 객체는 외부에서 data를 불러올 때 사용되는 객체로, 요청에 대한 응답 data가 비동기처리로 인해 제대로 전달되지 않았을 때 Promise 객체를 응답으로 전달함으로써 에러를 발생시키지 않게 됨
Promise 객체에는 Async/Await 을 붙일 수 있음 (다른 말로하면 Promise 객체에만 붙일 수 있음)
위와 같이 .then( ) 을 이용해서 요청에 대한 응답이 들어오면 해당 응답 data (res)를 어떻게 처리할 건지를 설정할 수 있고, return 문을 이용해 다음 요청을 바로 보낼 수 있음
*결과적으로 콜백지옥이 발생하지 않고, 코드의 가독성이 좋아짐
+a) Promise 객체에 사용할 수 있는 .then( ) 을 axios 에 사용할 수 있는 이유
axios 를 포함한 데이터 통신에 사용되는 현대의 라이브러리들은 대부분 Promise를 기반으로 만들어져 있음
따라서 axios는 Promise를 기반으로 만들어졌기 때문에 .then( ) 같은 메소드 활용이 가능하며, async/await 도 사용가능
[BUT!] => Promise 로도 해결되지 않는 문제가 있음!!
Promise의 문제점 2가지
1. 결과값을 상수에 바로 담지 못함
const onClickPromise = () => {
const result = axios
.get("http://numbersapi.com/random?min=1&max=200")
.then((res) => {
const num = res.data.split(" ")[0];
return axios.get(`https://koreanjson.com/posts/${num}`);
})
.then((res) => {
const userId = res.data.UserId;
// prettier-ignore
return axios.get(`https://koreanjson.com/posts?userId=${userId}`)
})
.then((res) => {
console.log(res.data);
});
console.log(result)
};

2. 코드 실행순서가 직관적이지 못함
const onClickPromise = () => {
console.log("여기는 1번입니다~");
axios
.get("http://numbersapi.com/random?min=1&max=200")
.then((res) => {
console.log("여기는 2번입니다~");
const num = res.data.split(" ")[0];
return axios.get(`https://koreanjson.com/posts/${num}`);
})
.then((res) => {
console.log("여기는 3번입니다~");
const userId = res.data.UserId;
// prettier-ignore
return axios.get(`https://koreanjson.com/posts?userId=${userId}`)
})
.then((res) => {
console.log("여기는 4번입니다~");
console.log(res.data);
});
console.log("여기는 5번입니다~");
};
Async/Await 실습
const onClickAsyncAwait = async () => {
console.log("여기는 1번입니다~");
// prettier-ignore
const res1 = await axios.get("http://numbersapi.com/random?min=1&max=200");
const num = res1.data.split(" ")[0];
console.log("여기는 2번입니다~");
const res2 = await axios.get(`https://koreanjson.com/posts/${num}`);
const userId = res2.data.UserId;
console.log("여기는 3번입니다~");
// prettier-ignore
const res3 = await axios.get(`https://koreanjson.com/posts?userId=${userId}`)
console.log(res3.data);
console.log("여기는 4번입니다~");
};
위의 Promise의 문제를 해결하기 위해 Async/Await 방식이 탄생함
코드를 읽어가는 과정에서 await을 만나는 순간 해당 await이 속한 async 함수 자체가 queue에 들어가게 됨
*정확히는 await으로 기다릴 API 요청 (여기서는 axios.get~) 이 queue에 먼저 담기고, 이후 async 함수가 queue에 담기게 됨
요청에 대한 응답을 기다린 이후 다음 코드가 실행되기 때문에 코드 상의 순서대로 실행이 됨
MacroTaskQueue = setTimeout, setInterval 등이 들어가는 queue
MicroTaskQueue = Promise 등이 들어가는 queue
비동기 처리로 Queue로 들어가는 것들은 다시 Macroqueue 와 Microqueue 로 나눠져서 들어가게 됨
여기서 모든 stack이 빠져나가면 queue에서 처리되는 것들 중 Microqueue에 있는 것이 Stack으로 빠져서 우선 실행됨
import axoios from 'axios'
export default function IsSubmitting(){
const [isSubmitting,setIsSubmitting] = useState(false)
const onClickSubmit = async ()=>{
// 등록하는 동안은 등록버튼이 작동하지 않도록
setIsSubmitting(true)
const result = await axios.get("https://koreanjson.com/posts/1")
// 등록이 완료되었다면 다시 버튼이 동작하도록
setIsSubmitting(false)
}
return(
<button onClick={onClickSubmit} disabled={isSubmitting}> 등록하기 등의 API 요청버튼 </button>
)
}
isSubmittg 은 값이 바뀐 적이 없어야 함[이유]
-1. 코드를 읽어내려가는 과정에서 await을 만나는 순간 해당 await을 감싸는 async 함수 전체가 Microqueue에 들어가게 됨
-2. 여기서 함수는 한 번 종료되기 때문에 state의 값이 true로 바뀌게 됨
-3. 이후 stack이 모두 비어진 상태가 되면, Microqueue에 있는 await으로 기다림 되고 있는 요청처리가 먼저 실행되고, 이후 async 함수가 실행됨
(여기서 async는 종료된 이후 부분부터 읽어내려감)
-4. async 함수가 처리되고 종료되면 다시 한 번 state의 값이 false로 바뀜