Какво е Fetch Api в Javascript

API за извличане ни позволява да изпращаме заявки до сървъра и да получаваме обратни отговори. Той също така предоставя глобален метод fetch(), който улеснява асинхронното получаване на ресурси в мрежата.

По-долу е даден прост пример за реагиращ компонент, който използва метода fetch, за да получи данни от сървъра. Компонентът „Продукти“ получава „URL“ като проп. Вътре в куката useEffect се прави извикването за извличане и състоянието се актуализира, което след това изобразява списъка с извлечени елементи.

const Products = ({ url }) => {
  const [data, setData] = useState();
  const [error, setError] = useState(false);

  useEffect(() => {
    fetch(url)
      .then(response => setData(response.data))
      .catch(error => setError(true))
  }, [url])

  if (!data) {
    return <p>No Products Found</p>
  }
  return (
    <>
      {
        data.map(item => <li key={item.id}>{item.name}</li>)
      }
    </>      
  )
}

Проблеми с горния код

Горният код ще работи перфектно в повечето случаи, но какво ще стане, ако компонентът се демонтира или нашият URL адрес се променя многократно бързо? Например, ако нашият URL се промени 3 пъти бързо, методът за извличане ще бъде извикан 3 пъти с 3 различни URL адреса и заявките ще отнемат различно време, за да получат обратно данни от сървъра.

Така че като се има предвид, че задействаме всяка заявка през 100 ms една от друга,

Първа заявка (необходими са 100 ms за връщане на данни)
Изстреляно на = 0ms
Получава обратен отговор на = 0ms + 100ms = 100ms

Втора заявка (необходими са 500 ms за връщане на данни)
Изстреляно при = 100 ms
Получава обратен отговор при = 100 ms + 500 ms = 600 ms

Трета заявка (отнема 200 ms за връщане на данни)
Изстреля се на = 200 ms
Получава обратен отговор на = 200 ms + 200 ms = 400 ms

За горния пример при 100 ms ще получим обратно нашия първи отговор. След това на 400 ms ще пристигне третият отговор и накрая, на 600 ms ще пристигне вторият отговор. Така че в действителност, ако изстреляме всичките тези 3 заявки за извличане, ще завършим с данните от втората, тъй като пристигането им отне малко повече време. Имаме данните от втория URL адрес, а не от третия URL адрес, което означава, че данните ни не са синхронизирани.

Това е много често срещан случай на употреба, тъй като заявките отнемат различно време в мрежата по множество причини.

Решението на проблема

За да избегнем този случай на употреба, можем да отменим заявката за извличане всеки път, когато URL адресът се промени, като използваме функцията за почистване useEffect, за да прекратим заявката за извличане. Използвайки AbortController и подавайки сигнала на контролера в заявката за извличане, можем да закачим AbortController със заявката за извличане. Тогава просто във функцията за почистване можем да извикаме функцията controller.abort(), за да отменим заявката, когато URL адресът се промени. Така че целият код вътре в блоковете then() и catch() изобщо няма да се изпълнява.

Ето пример за актуализираната кука

  useEffect(() => {
    const controller = new AbortController()
    fetch(url, { signal: controller.signal })
      .then(response => setData(response.data))
      .catch(error => setError(true));
    
    return () => {
      controller.abort();
    }
  }, [url])

Заключение

Така че независимо колко пъти се променя URL адресът, данните ще бъдат синхронизирани с последния URL адрес, тъй като преди да бъде направено ново повикване за извличане, предишното се прекъсва напълно.

Благодаря и се надявам да ви е харесало четенето на тази статия.