14/02/2007
I en verden af moderne webudvikling, hvor applikationer konstant kommunikerer med servere og eksterne API'er, er fejl uundgåelige. Netværksforbindelser kan være ustabile, servere kan være midlertidigt overbelastede, og API'er kan have et øjebliks hikke. Som udviklere er det vores ansvar at bygge systemer, der ikke blot fungerer under ideelle forhold, men som også kan håndtere disse uforudsete problemer med elegance. Her kommer RxJS (Reactive Extensions for JavaScript) og dens kraftfulde operatører ind i billedet. En af de mest essentielle operatører til at skabe modstandsdygtige applikationer er retry. Denne artikel dykker ned i, hvad 'retry'-operatøren er, hvordan den fungerer, og hvordan du kan bruge den til at forbedre dine applikationers pålidelighed markant.

Hvad er RxJS 'retry' Operatøren?
Kort fortalt er retry en pipeable operator i RxJS, der er designet til at håndtere fejl, som en observable måtte udsende. Som navnet antyder, er dens primære funktion at forsøge igen. Når en kilde-observable udsender en fejl, i stedet for at lade fejlen propagere ned gennem streamen og afslutte den, vil retry-operatøren fange fejlen og gen-abonnere på kilde-observablen. Dette starter effektivt operationen forfra.
Operatøren kan konfigureres med et tal, der angiver det maksimale antal genforsøg. For eksempel:
import { retry } from 'rxjs/operators'; myObservable$.pipe( retry(3) // Forsøger den oprindelige + 3 genforsøg ).subscribe(...);I dette eksempel, hvis myObservable$ fejler, vil retry(3) automatisk gen-abonnere på den op til tre gange. Det betyder, at den samlede operation vil blive forsøgt op til fire gange (den oprindelige + tre genforsøg). Hvis alle disse forsøg fejler, vil fejlen til sidst blive sendt videre til abonnentens fejl-callback.
Hvis du kalder retry() uden et argument, vil den forsøge uendeligt mange gange. Dette kan være nyttigt i visse scenarier, såsom at opretholde en WebSocket-forbindelse, men det skal bruges med stor forsigtighed for at undgå uendelige løkker, der kan overbelaste en server eller låse en klients browser.

Et Praktisk Eksempel: Genforsøg af HTTP-anmodninger i Angular
Den mest almindelige anvendelse af retry er i forbindelse med HTTP-anmodninger. Forestil dig en Angular-service, der henter data fra et API. Netværket kan være ustabilt, eller serveren kan returnere en midlertidig fejl (f.eks. en 503 Service Unavailable).
Uden fejlhåndtering ser en simpel service-metode måske sådan ud:
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class DataService { private apiUrl = 'https://api.example.com/data'; constructor(private http: HttpClient) {} getData() { return this.http.get(this.apiUrl); } }Hvis dette HTTP-kald fejler én gang, vil den observable, der returneres af getData(), straks udsende en fejl. Komponenterne, der abonnerer på denne service, vil modtage fejlen og skal selv håndtere den. Brugeren vil sandsynligvis se en fejlmeddelelse med det samme.
Lad os nu forbedre den med retry for at gøre den mere robust:
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { retry } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class DataService { private apiUrl = 'https://api.example.com/data'; constructor(private http: HttpClient) {} getData() { return this.http.get(this.apiUrl).pipe( retry(2) // Forsøg den oprindelige anmodning + 2 genforsøg ved fejl ); } }Med denne lille ændring har vi transformeret vores service. Hvis det første HTTP-kald fejler på grund af en midlertidig netværksfejl, vil brugeren ikke bemærke noget. RxJS vil i baggrunden automatisk sende anmodningen igen. Og hvis den også fejler, vil den prøve en sidste gang. Først hvis alle tre forsøg (1 original + 2 genforsøg) fejler, vil fejlen blive sendt til komponenten. Dette skaber en meget bedre og mere problemfri brugeroplevelse og gør applikationen markant mere pålidelig.

'retry' vs. 'catchError': Valg af den Rette Strategi
retry er ikke den eneste operatør til fejlhåndtering. En anden meget almindelig operatør er catchError. Det er vigtigt at forstå forskellen for at vælge den rigtige strategi til en given situation.
- retry: Bruges, når du forventer, at fejlen er midlertidig, og at den samme operation sandsynligvis vil lykkes, hvis den blot forsøges igen. Målet er at få et succesfuldt resultat fra den *oprindelige* observable.
- catchError: Bruges, når du vil håndtere en fejl på en specifik måde. Du ønsker ikke nødvendigvis at gentage den oprindelige operation. I stedet vil du måske levere en standardværdi (f.eks. fra en cache), logge fejlen til en monitoreringstjeneste, eller transformere fejlen til en mere brugervenlig besked, før den når brugeren.
catchErrorgiver dig mulighed for at erstatte den fejlende stream med en ny, succesfuld observable.
Her er en sammenligningstabel for at illustrere forskellene:
| Funktion | retry | catchError |
|---|---|---|
| Primært Formål | At gen-abonnere på den samme kilde-observable. | At fange en fejl og erstatte den med en ny observable eller kaste en ny fejl. |
| Resultat ved Fejl | Forsøger operationen igen. | Giver kontrol til en callback-funktion for at bestemme, hvad der skal ske. |
| Typisk Anvendelse | Midlertidige netværksfejl, serverfejl (5xx). | Klientfejl (4xx, f.eks. 404 Not Found), levere fallback-data, fejl-logging. |
| Returnerer | Returnerer en observable, der spejler kilden, men genforsøger ved fejl. | Returnerer en observable, der kan skifte til en ny stream ved fejl. |
Ofte ser man dem brugt sammen. Man kan først bruge retry til at håndtere midlertidige fejl, og hvis alle genforsøg fejler, kan man bruge catchError til at håndtere den endelige fejl på en pæn måde.
Avancerede Overvejelser og Bedste Praksis
Selvom retry er simpel at bruge, er der et par vigtige overvejelser for at bruge den korrekt.

Idempotente Operationer
Du bør primært bruge retry på operationer, der er idempotente. En idempotent operation er en, der kan udføres flere gange uden at ændre resultatet ud over den første udførelse. HTTP GET, PUT, og DELETE-anmodninger er typisk idempotente. En HTTP POST-anmodning, der opretter en ny ressource, er generelt *ikke* idempotent. Hvis du automatisk genforsøger en POST-anmodning, risikerer du at oprette flere identiske ressourcer, hvilket kan føre til alvorlige datafejl. Brug derfor retry med stor omtanke på ikke-idempotente operationer.
Forsinkelse mellem Genforsøg (Exponential Backoff)
Den simple retry-operatør forsøger igen med det samme. Hvis en server er midlertidigt overbelastet, kan øjeblikkelige genforsøg gøre situationen værre. En mere avanceret fejlhåndtering-strategi er at indføre en forsinkelse mellem hvert forsøg, ofte med en stigende varighed (kendt som 'exponential backoff'). Dette giver serveren tid til at komme sig. Den simple retry understøtter ikke dette direkte. For at opnå dette skal du bruge den mere avancerede retryWhen-operatør, som giver dig fuld kontrol over logikken for, hvornår og hvordan der skal genforsøges.
Ofte Stillede Spørgsmål (FAQ)
- Hvor mange gange vil `retry(2)` køre i alt?
- Den vil køre den oprindelige anmodning én gang, og hvis den fejler, vil den forsøge igen op til to gange. Det giver et samlet maksimum på tre forsøg.
- Hvad sker der, hvis alle genforsøg med `retry` fejler?
- Efter det sidste genforsøg er mislykket, vil
retry-operatøren lade fejlen passere videre ned i observable-streamen. Fejlen vil derefter blive håndteret af den næste fejlhåndteringsmekanisme, f.eks. encatchError-operatør eller `error`-callback i din `subscribe`-metode. - Kan jeg bruge `retry` til andet end HTTP-kald?
- Absolut.
retrykan bruges på enhver observable. Det kan være en WebSocket-forbindelse, der mister forbindelsen, en kompleks beregning, der er afhængig af en ustabil ressource, eller enhver anden asynkron operation, der kan fejle midlertidigt. - Hvordan tilføjer jeg en forsinkelse mellem genforsøg?
- For at tilføje en forsinkelse skal du bruge den mere komplekse
retryWhen-operatør i kombination med andre operatører somdelayWhenogtimer. Dette giver dig mulighed for at implementere avancerede strategier som exponential backoff.
Konklusion
RxJS' retry-operatør er et simpelt, men utroligt kraftfuldt værktøj i enhver moderne webudviklers arsenal. Ved at tilføje en enkelt linje kode kan du transformere en skrøbelig datahentning til en robust operation, der elegant håndterer midlertidige netværks- og serverfejl. Det forbedrer ikke kun applikationens tekniske pålidelighed, men skaber også en langt bedre og mindre frustrerende oplevelse for slutbrugeren. Ved at forstå, hvornår man skal bruge retry i forhold til catchError, og ved at være opmærksom på faldgruber som ikke-idempotente operationer, kan du bygge applikationer, der er forberedt på den uforudsigelige virkelighed på internettet.
Hvis du vil læse andre artikler, der ligner RxJS Retry: Robust Fejlhåndtering i Praksis, kan du besøge kategorien Sundhed.
