- admin
- API error, błędy sieciowe, exponential backoff, fetch, JavaScript API, localStorage, obsługa offline, offline storage
- 0 Comments
- 19788 Views
Wprowadzenie
Każda aplikacja webowa polega na API. Ale co, jeśli internet zawiedzie, serwer zwróci błąd, albo użytkownik przełączy się z Wi-Fi na LTE?
W naszym projekcie odkryliśmy, że nawet chwilowy brak połączenia oznaczał utratę danych użytkownika – formularze nie zapisywały się, a requesty „umierały” w ciszy.
Problem
- Użytkownicy często tracili internet na sekundę czy dwie.
fetch()w JavaScript od razu rzucał wyjątek.- Brak retry oznaczał, że dane nie trafiały na serwer.
- Efekt: frustracja klientów i „czarne dziury” w bazie.
Przykład prostego fetch, który zawodził:
async function saveData(data) {
const response = fetch('/api/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error('Błąd zapisu: ' + response.status);
}
}
Ten kod działał tylko wtedy, gdy internet był stabilny. Wystarczyło chwilowe przerwanie połączenia – i koniec.
Rozwiązanie: Retry z Exponential Backoff
Zdecydowaliśmy się na retry mechanizm:
- pierwsza próba → jeśli błąd → poczekaj chwilę i spróbuj ponownie,
- każdy kolejny retry ma rosnący czas oczekiwania (exponential backoff).
- np. 1s → 2s → 4s → 8s…
Przykładowa implementacja:
async function fetchWithRetry(url, options = {}, retries = 5, delay = 1000) {
try {
const response = fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response;
} catch (err) {
if (retries > 0) {
console.warn(`Błąd: ${err.message}. Próba ponowna za ${delay} ms...`);
await new Promise(res => setTimeout(res, delay));
return fetchWithRetry(url, options, retries - 1, delay * 2);
} else {
throw new Error('Wyczerpano wszystkie próby połączenia.');
}
}
}
Użycie:
fetchWithRetry('/api/save', {
method: 'POST',
body: JSON.stringify({ name: 'Jan', email: '[email protected]' }),
headers: { 'Content-Type': 'application/json' }
})
.then(res => console.log('Sukces!', res.status))
.catch(err => console.error('Nie udało się wysłać:', err.message));
Rozszerzenie: Obsługa offline (Service Worker + localStorage)
Dodatkowo dodaliśmy fallback:
- Jeśli brak internetu, dane zapisywały się lokalnie (w
localStorage). - Po odzyskaniu sieci – serwis synchronizował dane.
Prosty przykład:
function saveOffline(data) {
let queue = JSON.parse(localStorage.getItem('offlineQueue') || '[]');
queue.push(data);
localStorage.setItem('offlineQueue', JSON.stringify(queue));
}
window.addEventListener('online', async () => {
let queue = JSON.parse(localStorage.getItem('offlineQueue') || '[]');
for (let data of queue) {
await fetchWithRetry('/api/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
}
localStorage.removeItem('offlineQueue');
});
Fun Facts
- Podczas testów wyłączyliśmy Wi-Fi w połowie requestu – nasz nowy kod dzielnie próbował jeszcze 5 razy zanim się poddał.
- Jeden z testerów żartował, że aplikacja jest teraz „bardziej cierpliwa niż użytkownicy”.
- Dzięki mechanizmowi retry odsetek utraconych requestów spadł z ~7% do poniżej 0,5%.
Efekt końcowy
Po wdrożeniu:
- Użytkownicy nie tracą już danych przy chwilowym braku internetu.
- Requesty powtarzają się automatycznie i trafiają na serwer.
- UX stał się płynniejszy, a wsparcie techniczne dostało mniej zgłoszeń.
Chcesz wdrożyć podobne rozwiązanie w swojej firmie?
Przekładamy wiedzę z bloga na konkretne działania biznesowe: analizę, wdrożenie i rozwój. Sprawdź usługę i zobacz, jak możemy pomóc także u Ciebie.

