[JavaScript] 배열에서 Promise Result 값 가져오기

김방울·2023년 5월 31일
0

JavaScript

목록 보기
6/7
post-thumbnail

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 반복문을 사용하면 순차적으로 비동기 함수를 실행시킬 수 있으나, 대신 병렬적으로 실행하는 것보다 속도가 느리다고 합니다.

오늘도 이렇게 삽질을 하며 하나를 배워...갑니다..🐒

참고자료

profile
코딩하는 고양이🐱 / UI Developer, Front-end Developer

0개의 댓글