- Long polling simulerer server-push over HTTP, men lider af header-overhead, timeout-fejl og ressourcebegrænsninger.
- Proxyer, grænser for browserforbindelse og caching kan lydløst afbryde eller forringe funktionsmåden ved lange polling-processer i stor skala.
- Protokoller som Bayeux og BOSH bygger på long polling for at tilbyde tovejsmeddelelser, ofte sammen med streaming.
- Optimerede timeouts, batching, komprimering og omhyggelig gentagelseslogik er afgørende for at holde JavaScript's long polling robust.
Lang polling i JavaScript ser bedragerisk simpel ud: hold en HTTP-anmodning åben, indtil serveren har noget at sige, returner dataene, og åbn straks en ny anmodning. Udefra føles det næsten som realtidskommunikation, så det er ikke overraskende, at chat-apps, dashboards, spil og notifikationssystemer har været afhængige af det i årevis. Men under motorhjelmen er der små faldgruber i forhold til ydeevne, skalerbarhed og pålidelighed, der bider sig fast, så snart din app vokser ud over en håndfuld brugere.
At forstå de reelle problemer med long polling i JavaScript kræver, at man går ud over den grundlæggende beskrivelse af "klient sender anmodning, server venter, server svarer" og ser på HTTP-semantik, browsergrænser, proxyer, timeouts og alternativer som WebSockets, HTTP-streaming og Server-Sent Events. I denne guide vil vi dykke ned i, hvor længe afstemninger fungerer, hvilke problemer IETF og større leverandører har identificeret, og hvordan man beslutter, hvornår man skal bruge dem, hvornår man skal optimere dem, og hvornår man skal erstatte dem helt.
Hvad long polling i JavaScript egentlig er

Kort sagt er long polling et mønster bygget oven på normal HTTP, hvor browseren sender en anmodning, og serveren bevidst forsinker svaret, indtil nye data er tilgængelige, eller en timeout udløber. Så snart browseren modtager et svar, behandler den dataene og affyrer straks en ny anmodning, hvorved forbindelsen "næsten altid" forbliver i afventning.
Denne proces er meget forskellig fra klassisk short polling, hvor klienten med et fast interval hamrer serveren og spørger "noget nyt?", uanset om der er opdateringer. Med lang polling holder serveren anmodningen åben i perioder med inaktivitet, så klienten ikke behøver at sende tomme polls gentagne gange, når intet har ændret sig.
En typisk JavaScript-lang polling-løkke ligner en rekursiv subscribe-funktion, der kalder fetch (eller XMLHttpRequest), venter på svaret, håndterer nyttelasten og derefter kalder sig selv igen. Hvis netværket falder ned, eller der opstår en fejl, forsøger koden at oprette forbindelse igen med det samme eller efter en kort forsinkelse, og forsøger at holde den ventende forbindelse i live så meget som muligt.
Denne fremgangsmåde fungerer særligt godt, når meddelelser er relativt sjældne, fordi inaktiv tid på forbindelsen ikke producerer netværkstrafik eller CPU-belastning ud over omkostningerne ved at holde en socket åben. Brugeren oplever næsten øjeblikkelige opdateringer, mens serveren undgår at blive bombarderet med konstante polling-tjek.
Men så snart begivenheder bliver hyppige, har hver begivenhed en tendens til at udløse en hel HTTP-svarcyklus – statuslinje, headere, godkendelse, brødtekst – så omkostningerne pr. besked kan eksplodere, og trafikmønsteret begynder at ligne en savtand af anmodnings-/svarstigninger. Det er her, problemer med skalering og overhead begynder at dukke op i apps i den virkelige verden.
Korte afstemninger vs. lange afstemninger vs. streaming

For virkelig at forstå problemstillingen, er det nyttigt at sætte long polling op mod både short polling og HTTP-streaming, da alle tre er måder at fake "push" over en fundamentalt request/response-protokol som HTTP.
Kort polling er den naive variant: klienten sender med jævne mellemrum HTTP-anmodninger (f.eks. hvert 5.-10. sekund), og serveren svarer straks med alle beskeder, der er ankommet siden den sidste poll. Hvis der ikke er noget tilgængeligt, svarer serveren stadig, ofte med en tom nyttelast, og klienten venter blot til det næste interval.
Denne model har to åbenlyse ulemper: Meddelelsesforsinkelsen kan være lige så høj som pollingintervallet, og serveren er tvunget til at håndtere gentagne anmodninger, selv når der ikke er nogen nye data. For små systemer eller systemer med lav trafik kan dette være acceptabelt, men i stor skala spilder det CPU-cyklusser, netværksbåndbredde og kan medføre mærkbare forsinkelser for brugerne.
Long polling forbedrer dette ved at lade serveren "holde åben" hver anmodning, indtil der sker noget interessant - en begivenhed, der skal leveres, eller en timeout-tærskel. Latenstiden for nye beskeder er nu tæt på en enkelt netværksrundtur, og tomme svar sker meget sjældnere, hvilket reducerer spildt trafik.
HTTP-streaming går et skridt videre ved aldrig at lukke svaret overhovedet: serveren sender ét HTTP-svar og streamer derefter flere databidder over den samme forbindelse, adskilt af en vis framing på applikationsniveau. Klienten læser strømmen trinvis i stedet for at vente på, at hele svaret er færdigt.
I HTTP/1.1 implementeres dette normalt via chunked transfer encoding, hvor serveren indstiller Transfer-Encoding: chunked og derefter sender hvert stykke data som et separat chunk med sin egen længdeheader. I HTTP/1.0-stilopsætninger kan streaming også opnås ved at udelade Content-Length og bruge connection close som end-of-stream-markør.
Den afgørende forskel er, at long polling lukker svaret efter hver besked, hvilket fremtvinger en ny anmodning til den næste hændelse, mens streaming holder det samme svar i live og sender beskeder, når de opstår. Det har store konsekvenser for latenstid, hukommelsesforbrug, proxyer og hvordan du indrammer beskeder på applikationslaget.
Sådan fungerer HTTP long polling under motorhjelmen
Fra HTTP-protokollens perspektiv introducerer long polling ikke nye metoder eller statuskoder; det strækker blot den sædvanlige semantik for en anmodning, der venter på et svar. IETF's analyse af "bidirektionel HTTP" gør det klart, at long polling stadig er gyldig HTTP 1.0/1.1, men presser modellen tæt på sine grænser.
Den grundlæggende livscyklus for en lang polling-interaktion ser sådan ud: klienten sender en indledende anmodning og holder pause, serveren udsætter svaret, indtil en hændelse, statusændring eller timeout indtræffer, derefter returnerer serveren et fuldt HTTP-svar (ofte 200 OK) med hændelsesdataene, og til sidst sender klienten hurtigt en ny anmodning.
Dette mønster kan køre over enten persistente eller ikke-persistente HTTP-forbindelser; med persistente forbindelser undgår du overhead fra gentagne TCP-handshakes ved at genbruge den samme socket til flere lange afstemninger. I praksis er det næsten altid at foretrække at holde den samme TCP-forbindelse aktiv af hensyn til ydeevnen.
Servere, der implementerer long polling, skal normalt administrere to ressource-buckets pr. klient: selve TCP-forbindelsen og den udestående HTTP-anmodning, der venter i en kø eller et event-loop. Operativsystemer kan typisk håndtere et stort antal åbne sockets effektivt, men nogle HTTP-servere eller gateways allokerer betydelig hukommelse pr. anmodning, hvilket kan blive den virkelige flaskehals.
En interessant egenskab ved long polling er, at den under tung belastning har en tendens til at forringes elegant ved at øge latensen i stedet for at fejle hårdt. Hvis serveren er langsom, vil beskeder, der er beregnet til en klient, stå i kø, indtil et svar kan sendes; flere begivenheder i kø kan endda samles i et enkelt langt poll-svar, hvilket i øvrigt reducerer overhead pr. besked.
Problemer og begrænsninger ved lange afstemninger
Selvom long polling er meget udbredt og standardiseret i praksis, introducerer det adskillige velkendte tekniske problemer, som er lette at støde på fra JavaScript, hvis man ikke er forsigtig. Disse problemer viser sig i båndbreddeforbrug, latenstid, forbindelsesstyring og opførslen af mellemled som proxyer og cacher.
Header-overhead er den første omkostning, du støder på: hver lang poll-anmodning og -svar er en komplet HTTP-besked, typisk med cookies, auth-headere og andre metadata, der kan overskygge den faktiske nyttelast. For små, sjældne beskeder kan denne overhead være acceptabel, men i volumenbaserede faktureringsscenarier eller netværk med begrænset båndbredde kan forskellen mellem headerstørrelse og payloadstørrelse blive dyr.
Maksimal latenstid er et andet subtilt problem: mens den gennemsnitlige latenstid for lang polling for nye hændelser er omtrent én netværkstransit, kan den værst tænkelige forsinkelse være mere end tre transitter. Hvis en besked ankommer lige efter, at serveren har sendt et svar, skal serveren vente på den næste anmodning fra klienten, før den kan levere beskeden, og TCP-pakketab eller retransmission kan forlænge denne tid yderligere.
Etablering af forbindelser bliver ofte nævnt som en bekymring, især når folk sammenligner long polling med WebSockets. Hvis hvert langt polling-svar forårsagede, at HTTP-forbindelsen (og den underliggende TCP-forbindelse) blev lukket, ville omkostningerne ved gentagne gange at åbne sockets være enorme. Heldigvis kan og bør lang polling lægges oven på persistente forbindelser, så det korte mellemrum mellem svar ikke fortolkes som inaktivitet; dette lader transporten forblive åben og genanvendelig.
Server- og proxy-ressourceallokering er en væsentlig praktisk begrænsning: hver udestående anmodning bruger hukommelse og muligvis en tråd eller arbejder i synkrone arkitekturer. Mange ældre eller blokerende servere kan simpelthen ikke skalere til titusindvis af samtidige lange afstemninger, fordi deres samtidighedsmodel forventer, at hver anmodning fuldføres hurtigt. For disse systemer er asynkron I/O eller et hændelsesdrevet design næsten obligatorisk.
Cache-adfærd kan også afbryde lange polling-funktioner, hvis de ikke eksplicit undertrykkes. Mellemliggende cacher kan beslutte at genbruge eller gemme svar, medmindre du tydeligt markerer lange afstemningsanmodninger og svar med Cache-Control: no-cache (og relaterede headere), hvilket sikrer, at alle anmodninger rent faktisk når den oprindelige server og ikke modtager forældede data.
Timeouts, proxyer og mellemmandsadfærd
Et af de mest irriterende problemer i den virkelige verden med long polling i JavaScript er, at man ikke fuldt ud kontrollerer netværket mellem browser og server. Proxies, gateways, load balancers og endda browserstandarder pålægger alle timeouts eller bufferingstrategier, der kan bryde illusionen af en live forbindelse.
Lange afstemningsforespørgsler skal forblive åbne indtil en begivenhed eller en timeout, som du kontrollerer, men i virkeligheden vil mange formidlere afbryde forbindelserne efter en kortere, fastlagt periode. Selvom browsere som standard tillader op til omkring 300 sekunder, håndhæver nogle proxyer meget kortere timeouts, hvilket betyder, at din lange afstemning afsluttes med HTTP 504 Gateway Timeout eller blot en nulstilling, hvilket tvinger klienten til at genoprette forbindelse oftere, end du havde til hensigt.
Eksperimenter og operationelle erfaringer tyder på, at timeouts omkring 30 sekunder er et sikrere kompromis i mange miljøer, hvor 120 sekunder ofte fungerer, men er mere skrøbelige. Leverandører af netværksudstyr, der ønsker at være long-poll-venlige, opfordres til at bruge timeouts, der er betydeligt længere end typiske mellemlange netværkstransittider, så legitime long-pollings ikke afbrydes for tidligt.
Omvendte proxyer og forbindelsespooling introducerer en anden klasse af problemer. Nogle proxy-opsætninger deler en lille pulje af upstream-forbindelser mellem mange klienter; hvis lange afstemninger optager disse delte forbindelser i længere perioder, kan andre anmodninger blive udeladt eller sat i kø bag dem, hvilket underminerer ydeevnen for hele webstedet.
Med HTTP-streaming er mellemliggende buffering en endnu større risiko, fordi proxyer har tilladelse til at buffere delvise svar og kun videresende data, når de har akkumuleret et komplet svar. I så fald kan en streamingserver, der sender små JavaScript-bidder med forventet øjeblikkelig udførelse i browseren, opleve, at en mellemmand holder alt tilbage, indtil forbindelsen afsluttes, hvilket fuldstændigt ødelægger realtidsadfærden.
Browsergrænser og forbindelsesantal
Fra JavaScript manipulerer du aldrig direkte TCP-forbindelser; du ser kun konstruktioner på højt niveau som fetch eller XMLHttpRequest, men browsere håndhæver grænser for, hvor mange parallelle forbindelser der kan åbnes til den samme vært. Historisk set var denne grænse to forbindelser pr. oprindelse, hvilket gjorde Comet-lignende teknikker særligt vanskelige.
Moderne browsere har lempet disse begrænsninger til omkring seks eller otte forbindelser pr. vært, men der er stadig ingen standardmetode fra JavaScript til at spørge "hvor mange forbindelser er der tilbage?" eller til at koordinere brugen på tværs af faner og iframes. Hver fane kan uafhængigt oprette sin egen lange afstemning, hvilket hurtigt udtømmer de tilgængelige pladser og blokerer andre vigtige anmodninger som CSS, billeder eller API-kald.
En god praksis på serversiden er at bruge cookies eller en korrelationsmekanisme til at registrere flere lange afstemninger, der kommer fra den samme browserinstans, og undgå at udsætte dem alle. Ved at reagere hurtigt på ekstra lange afstemninger og kun hænge én eller et lille antal, kan du reducere risikoen for forbindelsesmangel eller pipeline-forstyrrelser.
Nogle protokoller på højere niveau, der er bygget på long polling, såsom Bayeux og BOSH, designer eksplicit omkring grænser for browserforbindelse. De bruger ofte højst to udestående HTTP-anmodninger ad gangen og undgår at bruge HTTP-pipelining, som er dårligt understøttet og ikke kan styres fra JavaScript.
HTTP-pipelining, framing og meddelelsesrækkefølge
Mens HTTP-pipelining (afsendelse af flere anmodninger back-to-back på den samme forbindelse, før der modtages svar) i teorien kunne reducere lang polling-latens, er det i praksis skrøbeligt og inkonsekvent implementeret. RFC 2616 er forsigtig med pipelining, især for POST-anmodninger, og formidlere eller klienter kan deaktivere det helt.
Protokoller, der forsøger at udnytte pipelining til long polling, skal først registrere, at pipelining understøttes pålideligt fra ende til anden; hvis ikke, falder de tilbage til konservativ, ikke-pipelineret adfærd. Fra en browsers JavaScript-miljø har du ikke hooks til at håndtere dette, så de fleste JavaScript-baserede long polling-stakke antager simpelthen, at pipelining ikke er tilgængelig.
Framing – den måde, du opdeler en kontinuerlig strøm af bytes i forskellige applikationsmeddelelser – er en anden subtil forskel mellem long polling og HTTP-streaming. Med long polling indeholder hvert HTTP-svar naturligt præcis én eller en batch af beskeder, så din framing er implicit: ét svar er lig med én del meningsfuld data.
I streaming kan du ikke stole på HTTP-chunk-grænser som din applikationsframing, fordi proxyer kan omdele datastrømmen, flette chunks eller opdele dem forskelligt. Det betyder, at appen skal integrere sine egne afgrænsere eller længdepræfikser i nyttelasten og parse i overensstemmelse hermed på klienten.
Meddelelserækkefølge og pålidelighed garanteres ikke af long polling i sig selv; de afhænger af din applikationsprotokol og dit lagringslag. Hvis en klient afbryder og genopretter forbindelsen midt i en strømmen, har du brug for eksplicitte mekanismer (som sekvensnumre eller ID'er for sidste hændelse) for at sikre, at ingen beskeder går tabt eller leveres i forkert rækkefølge.
Sikkerhedshensyn ved long polling i JavaScript
Long polling som teknik ændrer ikke HTTPs kernesikkerhedsmodel, men den måde, den ofte implementeres på – især i browsere, der bruger JavaScript – kan åbne døren for yderligere risici.
Mange løsninger til lang afstemning på tværs af domæner er afhængige af at udføre JavaScript, der returneres fra serveren, nogle gange via JSONP-lignende callbacks eller anden dynamisk scriptinjektion. Hvis din server er sårbar over for injektionsangreb, kan en angriber potentielt smugle vilkårlig kode ind i disse svar, som browseren derefter kører med sidens rettigheder.
Brug af HTTPS overalt er ufravigeligt: kryptering af transport beskytter mod manipulation og aflytning af langvarige forbindelser. Kombineret med robust godkendelse og autorisation (f.eks. tokenbaseret godkendelse og rollebaseret adgangskontrol) kan du begrænse lange polling-slutpunkter til de tilsigtede klienter.
Da long polling holder mange forbindelser åbne i lange perioder, kan det gøre DoS-angreb mere attraktive: en angriber kan forsøge at udtømme serverressourcer ved at åbne mange falske long polls. Hastighedsbegrænsning, forbindelseskvoter pr. IP eller token og fornuftige timeouts er vigtige defensive foranstaltninger.
Browserspecifikke trusler som CSRF er normalt mindre centrale for rene XHR/fetch long-afstemninger, der ikke er afhængige af ambient-cookies, men hvis cookies er involveret, bør du stadig behandle disse slutpunkter, som du ville behandle enhver anden følsom API. SameSite-cookies, CSRF-tokens hvor det er relevant, og strenge CORS-politikker er alle en del af et hærdet setup.
Long polling vs. WebSockets og server-sendte hændelser
Fra en JavaScript-udviklers synspunkt er long polling, WebSockets og Server-Sent Events alle måder at opnå "realtidsfølelse"-funktioner, men afvejningerne er en del forskellige.
WebSockets etablerer en ægte persistent, fuld-duplex kanal mellem browser og server. Efter det indledende HTTP-opgraderingshandshake kan dataframes flyde begge veje uden overhead fra HTTP-headere for hver besked, hvilket minimerer latenstid pr. besked og båndbreddeforbrug.
Dette gør WebSockets ideelle til højfrekvente scenarier såsom multiplayer-spil, samarbejdsredigering eller telemetri-streams, hvor beskederne er små, men hyppige. Ulempen er, at nogle virksomhedsfirewalls, gamle proxyer eller ældre klienter stadig ikke fungerer godt sammen med WebSocket-opgraderinger, og servere skal bruge en anden programmeringsmodel og infrastruktur, der er indstillet til et stort antal langlivede sockets.
SSE er enklere end WebSockets, når du kun har brug for server-til-klient push, men det kan ikke sende beskeder fra browseren tilbage via den samme kanal; du er stadig afhængig af normale HTTP-anmodninger til klient-til-server-kommunikation. Det kræver også, at formidlere tolererer streaming og ikke overdrevent bufferer delvise svar.
Sammenlignet med disse er long polling ofte den "laveste fællesnævner", der fungerer i flere miljøer, herunder ældre browsere og konservative netværksopsætninger. Derfor har mange realtidsplatforme historisk set brugt long polling som en reserve, når WebSockets blev blokeret, og gradvist opgraderet forbindelser, efterhånden som mulighederne tillod det.
Protokoller fra den virkelige verden oven i long polling: Bayeux, BOSH, SSE-lignende API'er
Adskillige protokoller på højere niveau er blevet bygget oven på long polling og streaming for at skjule kompleksiteten for applikationsudviklere og give en ensartet API. At forstå, hvordan de bruger long polling, hjælper med at afklare både teknikkens styrker og svagheder.
Bayeux-protokollen, som blev populariseret af CometD-projektet, understøtter både HTTP long polling og streaming som transportmuligheder. En Bayeux-klient bruger typisk to HTTP-forbindelser til en server, så meddelelser kan flyde asynkront i begge retninger uden at blive blokeret bag lange poll-anmodninger.
Under et indledende handshake forhandler klient og server, hvilke tovejsteknikker der understøttes – long polling, streaming osv. – og klienten vælger en fra overlapningen. For JavaScript-klienter undgår Bayeux typisk at være afhængig af HTTP-pipelining og begrænser sig i stedet til to udestående anmodninger for at undgå problemer med browserforbindelsesgrænser.
BOSH (Bidirectional-streams Over Synchronous HTTP) kommer fra XMPP-verdenen og blev designet til at emulere en TCP-lignende session over HTTP ved hjælp af long polling. En BOSH-forbindelsesadministrator holder en klients anmodning åben og svarer kun, når den har data fra applikationsserveren. Så snart klienten modtager dette svar, sender den en ny anmodning, og holder mindst én ventende lang afstemning næsten hele tiden.
BOSH kan bruge et eller to parallelle HTTP-forespørgsel-svar-par, der forhandles i starten af sessionen, og administrerer dem omhyggeligt for at respektere browserforbindelsesgrænser, samtidig med at asynkron tovejsbeskeder stadig tillades. Det forbyder chunked transfer encoding for at undgå bufferingproblemer hos mellemled og tillader komprimering via Content-Encoding for effektivitet.
Server-Sent Events, selvom de typisk implementeres via streaming snarere end long polling, er tæt beslægtede i ånden. W3C-specifikationen beskriver brugen af tekst/event-stream-svar og foreslår at deaktivere HTTP-chunking, medmindre event-raten er høj nok, igen for at undgå visse buffering- og mellemliggende problemer, der ses ved streaming over HTTP/1.1.
Optimering og hærdning af long polling i JavaScript-apps
Hvis du beslutter dig for, at long polling er det rigtige valg eller en nødvendig reserve for din JavaScript-app, er der flere strategier til at gøre den mere effektiv, skalerbar og robust.
Først skal du omhyggeligt justere dine timeouts. For kort tid spilder du ressourcer på hyppige genopkoblinger og risikerer at buldre af klienter, der alle genopretter forbindelse på én gang; for lang tid øger du risikoen for at nå proxy- eller load balancer-grænser, hvilket forårsager mystiske afbrydelser. I praksis giver værdier i området 20-30 sekunder for WaitTimeSeconds (i API'er som Amazon SQS) og lignende timeouts på app-niveau ofte en god balance.
Overvej derefter at batch-generere hændelser på serversiden, når flere meddelelser er i kø for en klient. Levering af flere opdateringer i et enkelt langt afstemningssvar reducerer dramatisk overhead pr. besked og kan hjælpe systemet med at nedbrydes mere elegant under belastning ved at bytte latenstid for gennemløb.
Komprimering er en anden nem gevinst: aktivering af gzip eller lignende indholdskodning til JSON-nyttelaster over lange polling-perioder kan reducere båndbreddeforbruget, især når beskeder deler en gentagen struktur. Afvejningen er ekstra CPU-omkostninger til komprimering, men i mange virkelige implementeringer opvejes dette af reduceret netværksoverførselstid.
Fra JavaScript-siden er robust fejlhåndtering og gentagelseslogik ikke til forhandling. Din abonnementsløkke bør registrere netværksfejl, timeouts eller misdannede svar og forsøge igen med backoff i stedet for at belaste serveren. Eksponentiel backoff med jitter er et almindeligt mønster for at forhindre synkroniserede gentagelsesstorme under afbrydelser.
Endelig skal du være disciplineret med hensyn til oprydning af forbindelser, når komponenter afmonteres eller faner lukkes. Zombie-afstemningsløkker, der fortsætter med at køre i baggrunden, kan gnave på både klientressourcer og serverkapacitet, så sørg altid for at have en mekanisme til at annullere udestående hentninger eller afbryde controllere, når en visning ødelægges, eller en bruger navigerer væk.
Long polling i JavaScript er fortsat et effektivt værktøj til at bygge næsten realtidsfunktioner i miljøer, hvor WebSockets eller SSE ikke er tilgængelige, men det kommer med en række skjulte omkostninger omkring headers, timeouts, proxyer og ressourceforbrug, som du skal forstå og administrere, hvis du vil have din app til at skalere problemfrit.