react-query
로 외부 api 연동 작업을 하던 중 위와 같이 Promise 객체가 반환되면서 데이터를 제대로 가져오지 못하는 이슈가 있었습니다.
const summonerInfo = useQuery('summonerInfo',
async () => {
const res = await client.get(`/api/account`, {params: {name: inputName}});
return res.data;
},
...
async/await
키워드를 사용하면 Promise
가 처리될 때 까지 기다리고, Promise 객체 안의 result 값(우리가 원하는 데이터!)을 반환해 줍니다.
그런데,, await
키워드를 (떡칠 수준으로)사용해도 Promise 객체를 그대로 반환하는 일이 있었습니다😱
const matchDetailInfo = useQuery(['matchDetail', matchInfo.data],
async ()=> {
const dataList = await matchInfo.data.data?.map(async (el: string) => {
const res = await client.get(`api/match/detail`, {params: {matchId: el}});
const nameList = await res.data?.data.metadata?.participants.map(async (el : string) => {
const res = await client.get(`/api/account`, {params: {puuid: el}});
const name = await res.data.data?.gameName;
return await (name);
});
const data = {
nameList : await nameList,
time: moment(await res.data?.data.info?.gameStartTimestamp).format('YYYY.MM.DD HH:mm:ss')
};
return data;
});
return dataList;
},
코드가 참,,, 끔찍합니다😓
배열 matchInfo
의 데이터가 콜백함수의 파라미터로 들어가도록 .map
메서드를 사용해 주었습니다.
콜백함수 실행 시 주어진 파라미터로 api를 두 번 호출하고,
api를 호출한 결과값이 원소로 담긴 배열을 반환합니다.
실행시키면 이렇게 전부 이행되지 않은 Promise 상태로 출력이 되는데요.. 😇
Array.map()
, Array.filter()
등 배열을 반환해 주는 메서드 안에 비동기 함수를 사용하면 반환되는 배열은 비동기 함수로 이루어진 배열이 됩니다.
await 키워드가 기다려 주는 것은 Promise 객체입니다.
await은 배열을 기다려 주지 않습..니다😇
async function 선언은 AsyncFunction 객체를 반환하는 하나의 비동기 함수를 정의합니다.
배열 내 모든 Promise가 이행된 후 결과값이 반환되게 하려면 Promise.all
메서드를 사용해 주면 됩니다.
const matchDetailInfo = useQuery(['matchDetail', matchInfo.data],
async ()=> {
const dataList = await matchInfo.data.data?.map(async (el: string) => {
...(중략)
const data = {
nameList : await Promise.all(nameList),
time: moment(await res.data?.data.info?.gameStartTimestamp).format('YYYY.MM.DD HH:mm:ss')
};
return data;
});
return await Promise.all(dataList);
},
리턴값을 Promise.all로 감싸 주고
콘솔로 찍어 보면.. 정상적으로 Promise의 결과값을 받아옵니다!👏
const data = {
nameList : await Promise.allSettled(nameList),
time: moment(await res.data?.data.info?.gameStartTimestamp).format('YYYY.MM.DD HH:mm:ss')
};
추가로, 요즘에는 Promise.allSettled()
라는 메서드를 더 많이 쓴다고 합니다.😃
Promise.all()
과 동일하게 여러 개의 Promise를 병렬적으로 이행시키는 것은 같지만,
Promise.all()
은 배열에 있는 프로미스 중 하나라도 reject가 호출된다면 성공한(fulfilled) 프로미스 응답을 무시하고 바로 catch
문으로 빠져버리는 반면
Promise.allSettled()
는 배열로 받은 모든 프로미스의 성공 여부와 상관없이 전부 완료만 되었다면 결과를 배열로 리턴해 줍니다.
Promise.all()
과 Promise.allSettled()
두 메서드는 모두 병렬적으로 수행되기 때문에 실행 결과의 순서가 보장되지 않습니다.
const getWithForOf = async() => {
console.time("for of");
const data = []
for (const username of usernames) {
let dataUser = await simulationFetchData(username);
data.push(dataUser);
}
console.timeEnd("for of");
}
getWithForOf();
// 출처: https://betterprogramming.pub/how-to-use-async-await-with-map-in-js-5059043564e0
for of
반복문을 사용하면 순차적으로 비동기 함수를 실행시킬 수 있으나, 대신 병렬적으로 실행하는 것보다 속도가 느리다고 합니다.
오늘도 이렇게 삽질을 하며 하나를 배워...갑니다..🐒