Wie man ausstehende API-Anfragen abbricht, um korrekte Daten anzuzeigen

Avatar of Georgi Nikoloff
Georgi Nikoloff on

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Ich musste kürzlich ein Widget in React erstellen, das Daten von mehreren API-Endpunkten abruft. Wenn der Benutzer herumklickt, werden neue Daten abgerufen und in die Benutzeroberfläche eingepasst. Aber das verursachte einige Probleme.

Ein Problem wurde schnell deutlich: Wenn der Benutzer schnell genug herumklickte, wurden bei der Auflösung früherer Netzwerkanfragen die Benutzeroberfläche kurzzeitig mit falschen, veralteten Daten aktualisiert.

Wir können unsere UI-Interaktionen debouncen, aber das löst unser Problem grundsätzlich nicht. Veraltete Netzwerkanfragen werden aufgelöst und aktualisieren unsere Benutzeroberfläche mit falschen Daten, bis die letzte Netzwerkanfrage abgeschlossen ist und unsere Benutzeroberfläche mit dem endgültigen korrekten Zustand aktualisiert. Das Problem wird bei langsameren Verbindungen deutlicher. Außerdem bleiben wir mit nutzlosen Netzwerkanfragen zurück, die die Daten des Benutzers verschwenden.

Hier ist ein Beispiel, das ich zur Veranschaulichung des Problems erstellt habe. Es holt Spieleangebote von Steam über die coole Cheap Shark API unter Verwendung der modernen `fetch()`-Methode. Versuchen Sie, den Preisgrenzwert schnell zu ändern, und Sie werden sehen, wie die Benutzeroberfläche mit falschen Daten aufblitzt, bis sie sich endlich stabilisiert.

Die Lösung

Es stellt sich heraus, dass es eine Möglichkeit gibt, ausstehende asynchrone DOM-Anfragen mit einem `AbortController` abzubrechen. Sie können ihn verwenden, um nicht nur HTTP-Anfragen, sondern auch Event-Listener abzubrechen.

Die **`AbortController`**-Schnittstelle repräsentiert ein Controller-Objekt, das es Ihnen ermöglicht, eine oder mehrere Webanfragen nach Belieben abzubrechen.

Mozilla Developer Network

Die `AbortController`-API ist einfach: Sie stellt ein `AbortSignal` zur Verfügung, das wir in unsere `fetch()`-Aufrufe einfügen, so

const abortController = new AbortController()
const signal = abortController.signal
fetch(url, { signal })

Von nun an können wir `abortController.abort()` aufrufen, um sicherzustellen, dass unser ausstehender Fetch abgebrochen wird.

Schreiben wir unser Beispiel neu, um sicherzustellen, dass wir alle ausstehenden Fetches abbrechen und nur die neuesten vom API erhaltenen Daten in unsere App einpassen.

Der Code ist weitgehend derselbe mit wenigen wichtigen Unterschieden

  1. Es wird eine neue gecachte Variable `abortController` in einem `useRef` in der Komponente `` erstellt.
  2. Für jeden neuen Fetch wird dieser Fetch mit einem neuen `AbortController` initialisiert und sein entsprechendes `AbortSignal` abgerufen.
  3. Das abgerufene `AbortSignal` wird an den `fetch()`-Aufruf übergeben.
  4. Es bricht sich beim nächsten Fetch selbst ab.
const App = () => {
 // Same as before, local variable and state declaration
 // ...

 // Create a new cached variable abortController in a useRef() hook
 const abortController = React.useRef()

 React.useEffect(() => {
  // If there is a pending fetch request with associated AbortController, abort
  if (abortController.current) {
    abortController.abort()
  }
  // Assign a new AbortController for the latest fetch to our useRef variable
  abortController.current = new AbortController()
  const { signal } = abortController.current

  // Same as before
  fetch(url, { signal }).then(res => {
    // Rest of our fetching logic, same as before
  })
 }, [
  abortController,
  sortByString,
  upperPrice,
  lowerPrice,
 ])
}

Fazit

Das ist alles! Wir haben jetzt das Beste aus beiden Welten: Wir debouncen unsere UI-Interaktionen **und** wir brechen manuell veraltete ausstehende Netzwerkanfragen ab. Auf diese Weise stellen wir sicher, dass unsere Benutzeroberfläche einmal und nur mit den neuesten Daten von unserem API aktualisiert wird.