[JS] Fetch multiple data in delay

Suyeon·2021년 2월 6일
0

Javascript

목록 보기
29/31
post-thumbnail

Issue

최근에 서버에서 데이터를 받아올 때, 여러개의 endpoints에서 데이터를 한번에 가져와야하는 경우가 있었다. 한번에 10개의 endpoints에서 데이터를 받아와서 5초의 주기로 갱신을 해야했다.

내가 처음에 시도했던 방법은 Promise.all()을 사용해서 한번에 데이터를 비동기적으로 불러오는 방법이였는데, too many request(429 error) 에러를 마주하게 되었다. 왜냐하면 Promise.all()은 여전히 10번의 http request를 서버로 보내기 때문이다.

How to solve

이러한 현상을 해결하기 위해서, 먼저 각각의 fetch에 delay를 준 다음, await을 사용하여 endpoints를 돌면서(loop) 순서대로 데이터를 fetch할 수 있다. 즉 동기적으로 데이터를 series로 불러오는 것이다.

const delay = (ms = 1000) => new Promise(r => setTimeout(r, ms));

const getDataSeries = async items => {
  let results = [];
  for (let index = 0; index < items.length; index++) {
    await delay();
    const res = await fetch(items[index]);
    results.push({ name: res.name, data: res.data });
  }
  return results;
};

setTimeout vs setInterval

일정한 주기로 데이터를 갱신해야 하는 경우, setIntervalsetTimeout중 어떤 것이 더 적합할까?

그리고 일정한 주기로 데이터를 갱신해야 하는 경우, setInterval보다 setTimeout을 사용하도록 권장된다. setInterval을 사용하게 되면, 서버에서 데이터를 받아올 때 응답이 늦어지는 경우에도 일정하게 계속 다음 데이터를 fetch하기 때문에, 에러를 핸들링할 수가 없게된다. 하지만 setTimeout을 사용하면, 이전 데이터를 잘 받은 뒤에, recursive pattern을 사용해서 일정주기로 데이터를 fetch할 수 있다.

아래와 같이 catch에서 setTimeout을 호출하게 되면, 데이터를 불러오는 과정에서 문제가 생겨서 다음 데이터를 불러올 수 있다. 이렇게 하지 않을 경우에는, 데이터를 불러오는 과정에서 문제가 생기면, 다음 데이터의 fetch가 중단되어진다.

// useFetch custom hook
import { useEffect, useState, useCallback } from 'react';

import api from 'api/api';
import { Data, GroupData } from 'types/types';

type DataStatus = [GroupData, boolean, string];

const useFetch = (endpoints: string[]): DataStatus => {
  const [data, setData] = useState<GroupData>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  const fetchData = useCallback(
    async (endpoints: string[]): Promise<any> => {
      const temp: GroupData = [];

      try {
        await setLoading(false);
        for (let i of endpoints) {
          const res: Data = await api.spot(i);
          await temp.push(res);
        }
        await setData(temp);
        setTimeout(() => fetchData(endpoints), 5000); // (*)
      } catch (err) {
        await setError(error);
        setTimeout(() => fetchData(endpoints), 5000);  // (*)
      }
    },
    [error]
  );

  useEffect(() => {
    fetchData(endpoints);
  }, [fetchData, endpoints]);

  return [data, loading, error];
};

export default useFetch;
profile
Hello World.

1개의 댓글

comment-user-thumbnail
2022년 4월 6일

잘 보고 갑니당!

답글 달기