DedSec Project

JavaScript: fetch, timeout, abort και HTTP errors — DedSec Project

fetch: αποτυχία δικτύου και HTTP status δεν είναι το ίδιο Ξεχώρισε transport από application error Η fetch() απορρίπτει το promise σε ορισμένα σφάλματα δικτύου ή ακύρωσης, αλλά μια απάντηση…

fetch: αποτυχία δικτύου και HTTP status δεν είναι το ίδιο

Ξεχώρισε transport από application error

Η fetch() απορρίπτει το promise σε ορισμένα σφάλματα δικτύου ή ακύρωσης, αλλά μια απάντηση 404 ή 500 συνήθως ολοκληρώνει το promise με ok === false. Έλεγξε το status πριν χρησιμοποιήσεις το σώμα ως επιτυχημένα δεδομένα. Για πραγματικό timeout χρησιμοποίησε AbortController, όχι μόνο timer που εμφανίζει μήνυμα ενώ η σύνδεση συνεχίζει.

async function getJSON(url, ms = 3000) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), ms);
  try {
    const response = await fetch(url, {signal: controller.signal});
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return await response.json();
  } finally {
    clearTimeout(timer);
  }
}

Τι σημαίνει κάθε αποτυχία;

Η αποτυχία JSON parsing αφορά το σώμα της απάντησης και δεν είναι το ίδιο με 503. Η ακύρωση από χρήστη διαφέρει από πρόβλημα δικτύου ή χαλασμένο DNS. Μην επιστρέφεις απλώς κενό object σε όλες τις εξαιρέσεις· έτσι ένα outage μοιάζει με νόμιμη έλλειψη αποτελεσμάτων. Πρόβλεψε ποια μηνύματα πρέπει να εμφανίζονται για καθεμία περίπτωση.

Ελεγχόμενο εργαστήριο

Σε δικό σου τοπικό HTTP fixture δημιούργησε /ok με JSON, /missing με 404 και /slow με καθυστέρηση μεγαλύτερη από το όριο. Κατάγραψε object, ρητό HTTP error και abort. Σύγκρινε Network panel browser με μήνυμα εφαρμογής. Μην δοκιμάζεις επανειλημμένες καθυστερήσεις σε δημόσια ξένη υπηρεσία.

Σωστά όρια retry

Η επανάληψη GET συχνά είναι δυνατή. Εντολές που αλλάζουν δεδομένα απαιτούν idempotency και deduplication πριν επαναληφθούν αυτόματα. Βάλε περιορισμένο αριθμό προσπαθειών και backoff. Μη γράφεις μυστικά URL query parameters ή ιδιωτικές πληροφορίες σε logs.

Ιδιοκτησία ακύρωσης και ολοκλήρωση σώματος απόκρισης

Το fetch μπορεί να ολοκληρωθεί όταν φτάσουν κεφαλίδες ενώ το σώμα συνεχίζει να μεταφέρεται. Αν καθαρίσεις τότε το timeout, η ανάγνωση σώματος μπορεί να μείνει απεριόριστη. Διατήρησε τον υπεύθυνο ακύρωσης ώσπου να τελειώσει η απαραίτητη ανάγνωση. Διαχώρισε απόρριψη μεταφοράς από απόκριση HTTP: το 404 είναι απόκριση που χρειάζεται ρητή απόφαση κατάστασης.

Η ακύρωση πρέπει να προέρχεται από τη λειτουργία που κατέχει το αίτημα. Νέο ερώτημα αναζήτησης ακυρώνει το παλιό, αλλά μια παλιά απόρριψη δεν πρέπει να σβήσει νεότερο αποτέλεσμα. Το τοπικό δείγμα δείχνει ιδιοκτησία χωρίς επικοινωνία με διακομιστή.

let current = 0;
function begin() { const version = ++current; return value => version === current ? value : null; }
const oldResult = begin();
const newResult = begin();
if (oldResult("old") !== null || newResult("new") !== "new") throw Error("stale result accepted");
const controller = new AbortController();
controller.abort();
if (!controller.signal.aborted) throw Error("cancellation not observable");
console.log("stale response ignored");

Στον πραγματικό client δοκίμασε ακύρωση κατά τις κεφαλίδες και κατά το σώμα χωριστά. Περιόρισε αποκωδικοποιημένο μέγεθος μαζί με χρόνο. Το timeout δεν αναιρεί εγγραφή στον διακομιστή· η επανάληψη ακυρωμένης εγγραφής χρειάζεται ανάλυση idempotency όπως κάθε αβέβαιη απόκριση.

Τεχνική αναφορά