최근 코드를 작성하는 중에 한번에 여러개의 비동기 작업을 처리해야 할 일이 생겼다.
단순히 async awiat을 사용해 다음과 같이 구현할 수 있지만,
const data = await fetch("/api");
const data2 = await fetch("/api");
const data3 = await fetch("/api");
const data4 = await fetch("/api");
특정 변수를 배열에 담고 그 배열을 주소값 또는 파라미터로 넣어 api를 여러번 호출해야 하는 상황이었다.
const number_arr = [1, 2, 3, 4, 5];
const data = await fetch(`/api/${number_arr[0]}`);
const data2 = await fetch(`/api/${number_arr[1]}`);
const data3 = await fetch(`/api/${number_arr[2]}`);
const data4 = await fetch(`/api/${number_arr[3]}`);
때문에 아무런 의심없이 forEach를 활용해 비동기 작업을 처리했었다.
실제 더미api를 불러오는 코드로 새로 작성했다.
const number_arr = [1, 2, 3, 4, 5];
const response_arr: any = [];
number_arr.forEach(async (num) => {
const { data }: AxiosResponse = await axios.get(
`https://jsonplaceholder.typicode.com/todos/${num}`
);
response_arr.push(data.id);
});
반복문을 사용해 총 5번의 api를 호출했고 그 값중에 id값을 response_arr에 넣는다.
예상으로는 data의 id 값들인 1,2,3,4,5가 response_arr안에 담겨야 한다.
그러나 빈 값이 출력된다.
왜 그런 걸까?
제목에서부터 예상했듯이 forEach는 비동기함수를 지원하지 않는다. 더 정확하게는 기다리지 않는다는 말이 맞다.
즉 반복문을 돌며 짜여진 로직만 실행할 뿐, callback등의 작업이 끝날때까지 기다리지 않고 다음으로 넘어가기 때문이다.
쉽게말해 forEach문은 비동기를 실행하든 안하든 x도 신경안쓴다는 뜻이다.
위 코드에서는 api를 호출하고 response_arr에 담기 전에 다음 api를 호출하고 그 값을 response_arr에 담기전에 다음 api를 호출하고...... 결국 response_arr에 어떤 값을 담기 전에 반복문이 끝나게 되고 cosnole.log()가 실행되며 빈 값이 출력되는 것이다.
어떻게 해결할까?
방법은 크게 두가지가 존재한다.
하나는 for of문을 사용하는 것이다.
아래는 위 forEach문을 for of로 바꾼 코드이다.
for (let num of number_arr) {
const { data }: AxiosResponse = await axios.get(
`https://jsonplaceholder.typicode.com/todos/${num}`
);
response_arr.push(data.id);
}
console.log(response_arr);
또는 병렬처리를 원할 경우 map함수를 사용할 수가 있다.
다만 map을 사용할 경우 Promise.all을 사용해 모든 프로미스의 결과를 기다릴 필요가 있다.
const promise_arr = number_arr.map((num) => {
return axios
.get(`https://jsonplaceholder.typicode.com/todos/${num}`)
.then((response) => response.data.id);
});
Promise.all(promise_arr)
.then((response_arr) => {
console.log(response_arr);
})
.catch((error) => {
console.log(error);
});
궁금한 점
해당 포스트를 작성하는 중 궁금한 점이 생겨났다.
forEach는 비동기작업을 기다리지 않는데, react-query의 useMutation은 병렬작업을 위해 forEach를 사용하는데요?
다음과 같은 코드가 있다.
["/insert_item_log", "/insert_clan_bank"].forEach((addr) => {
mutate(
{
id:addr
},
{
onSuccess,
}
);
});
react-query의 useMutation을 사용하기 위한 mutate함수의 코드이다.
분명 forEach는 비동기작업을 기다리지 않는다는데 forEach를 사용해도 무방했다.
왜 그럴까.
'forEach' 메서드를 사용하는 경우에도 'mutate'함수는 비동기 함수지만 React Query의 내부 구현은 비동기 작업을 처리할 수 있도록 설계되어 있다.
React-Query는 각각의 'mutate'호출을 병렬로 처리하고, 각각의 비동기 작업이 완료될 때마다 상태를 업데이트한다.
따라서, 위 코드에서 'forEach'를 사용하여 'mutate'를 호출하는 경우, 각각의 비동기 작업이 동시에 실행되고 완료될 때마다 'onSucces'콜백이 실행된다.
이는 React-Query가 내부적으로 병렬 처리를 지원하기 때문이다.
React-Query는 내부적으로 프로미스 관리와 상태 업데이트를 처리하기 때문에 별도의 비동기 작업 처리 방식을 고려할 필요가 없다.
'forEach'를 사용하여 여러개의 'mutate'를 호출하더라도 React-Query는 각각의 비동기 작업을 병렬로 처리하고 상태를 업데이트하여 정확한 결과를 반환한다.
'자바스크립트' 카테고리의 다른 글
JS)User-Agent의 작동 방식 및 사용방법 (0) | 2023.06.24 |
---|---|
자바스크립트) 함수 내 d내장함수와 프로토타입 (0) | 2023.06.19 |
자바스크립트) 객체의 복사 (0) | 2023.05.30 |
React) 구글 로그인 - @react-oauth/google (0) | 2023.03.30 |
AWS S3 이미지 업로드 with React (0) | 2023.01.25 |