Forstå asynkron programmering med Promises og async/await, og hent data med fetch().
Hittil har JavaScript-koden vår kjørt synkront – instruksjon for instruksjon, fra topp til bunn. Men mange oppgaver i moderne webutvikling tar tid: hente data fra en server, laste bilder, vente på brukerinput. Hvis JavaScript måtte vente på at hver slik operasjon ble ferdig før den fortsatte, ville nettsiden fryse og bli ubrukelig.
Løsningen er asynkron programmering – en modell der tidkrevende operasjoner startes og kjører i bakgrunnen, mens resten av programmet fortsetter. Når operasjonen er ferdig, varsles programmet og kan behandle resultatet.
I dette kapittelet lærer vi om Promises, async/await og den kanskje viktigste praktiske ferdigheten i moderne JavaScript: å hente data fra API-er (Application Programming Interfaces) med fetch().
La oss se forskjellen mellom synkron og asynkron kode:
``javascript`
console.log("Steg 1");
console.log("Steg 2"); // Venter til steg 1 er ferdig
console.log("Steg 3"); // Venter til steg 2 er ferdig
// Utskrift: Steg 1, Steg 2, Steg 3 (i rekkefølge)
`javascript
console.log("Steg 1");
setTimeout(() => {
console.log("Steg 2 (etter 2 sekunder)");
}, 2000);
console.log("Steg 3");
// Utskrift: Steg 1, Steg 3, Steg 2 (etter 2 sek)
`
I det asynkrone eksemplet kjøres «Steg 3» før «Steg 2» fordi setTimeout er asynkron – den starter en timer i bakgrunnen og lar resten av koden kjøre videre. Når timeren utløper etter 2 sekunder, kjøres callback-funksjonen.
setTimeout` er det enkleste eksemplet på asynkron kode, men i praksis er det nettverksforespørsler (henting av data fra API-er) som er den viktigste bruken av asynkron programmering.
Et API er et grensesnitt som lar programmer kommunisere med hverandre etter bestemte regler. Web-API-er er tjenester som leverer data over internett via HTTP-forespørsler. Når JavaScript henter værdata, kartinformasjon eller nyheter fra en ekstern tjeneste, kommuniserer det med et API. API-er mottar forespørsler og returnerer data, vanligvis i JSON-format. De fleste moderne nettsider og apper bruker API-er for å hente og sende data.
fetch() er den innebygde JavaScript-funksjonen for å sende HTTP-forespørsler. Den er asynkron og returnerer et Promise:``javascript`
fetch("https://api.eksempel.no/data")
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error("Feil:", error);
});
La oss bryte ned hva som skjer:
1. fetch(url) sender en HTTP GET-forespørsel til URL-en.then(response => response.json())
2. tar svaret fra serveren og konverterer det fra JSON-tekst til et JavaScript-objekt.then(data => { ... })
3. lar deg bruke de konverterte dataene.catch(error => { ... })
4. fanger opp feil (f.eks. nettverksproblemer)
`javascript``
// Hent et tilfeldig vitseriat fra et åpent API
fetch("https://official-joke-api.appspot.com/random_joke")
.then(response => response.json())
.then(joke => {
console.log(joke.setup);
console.log(joke.punchline);
})
.catch(error => {
console.error("Kunne ikke hente vits:", error);
});
Denne koden sender en forespørsel til et API som returnerer tilfeldige vitser i JSON-format, konverterer svaret til et JavaScript-objekt, og skriver ut oppsett og poeng.
Et Promise er et JavaScript-objekt som representerer det fremtidige resultatet av en asynkron operasjon. Navnet «promise» (løfte) kommer av at objektet gir et løfte om at det vil levere et resultat senere. Et Promise kan ha tre tilstander: pending (operasjonen er i gang), fulfilled (operasjonen lyktes og har et resultat) eller rejected (operasjonen feilet). Du håndterer utfallet med .then() for suksess og .catch() for feil.
Promise-kjeder med .then() kan bli vanskelige å lese. Moderne JavaScript tilbyr async/await som gjør asynkron kode mye enklere:
``javascript
async function hentData() {
try {
const response = await fetch("https://api.eksempel.no/data");
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Feil:", error);
}
}
hentData();
`
Nøkkelord:
- async foran funksjonen markerer den som asynkron
- await pauser funksjonen til Promiset er fullført (kan bare brukes inne i async-funksjoner)try/catch
- håndterer feil på en oversiktlig måte
`javascript
// Med .then() (eldre stil)
fetch("https://api.eksempel.no/brukere")
.then(response => response.json())
.then(brukere => {
console.log(brukere);
})
.catch(error => {
console.error(error);
});
// Med async/await (moderne, anbefalt)
async function hentBrukere() {
try {
const response = await fetch("https://api.eksempel.no/brukere");
const brukere = await response.json();
console.log(brukere);
} catch (error) {
console.error(error);
}
}
``
Begge gjør nøyaktig det samme, men async/await-versjonen er lettere å lese fordi den ser ut som vanlig synkron kode.
Nettverksforespørsler kan feile av mange grunner: ingen internettforbindelse, serveren er nede, feil URL, API-nøkkel er ugyldig, osv. God feilhåndtering er derfor avgjørende.
fetch() kaster bare en feil ved nettverksproblemer – den anser HTTP-feil som 404 og 500 som vellykkede forespørsler. Du må sjekke statuskoden selv:
``javascript
async function hentData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP-feil: " + response.status);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Noe gikk galt:", error.message);
return null;
}
}
`
response.ok er true for statuskoder 200-299 og false for andre. Ved å kaste en feil med throw new Error() sikrer vi at feil fanges av catch-blokken.
`javascript
async function lastInnVarsel() {
const statusDiv = document.querySelector("#status");
statusDiv.textContent = "Laster data...";
try {
const response = await fetch("https://api.met.no/weatherapi");
if (!response.ok) {
throw new Error("Kunne ikke hente værdata.");
}
const data = await response.json();
statusDiv.textContent = "";
visVardata(data);
} catch (error) {
statusDiv.textContent = "Feil: " + error.message;
statusDiv.style.color = "red";
}
}
``
Hittil har vi bare hentet data (GET). For å sende data til en server bruker vi POST-forespørsler:
``javascript
async function sendKommentar(tekst) {
try {
const response = await fetch("https://api.eksempel.no/kommentarer", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
innhold: tekst,
tidspunkt: new Date().toISOString(),
}),
});
if (!response.ok) {
throw new Error("Kunne ikke sende kommentar.");
}
const resultat = await response.json();
console.log("Kommentar lagret:", resultat);
} catch (error) {
console.error("Feil:", error.message);
}
}
`
fetch() tar et valgfritt options-objekt som andre argument:method
- : HTTP-metoden (POST, PUT, DELETE osv.)headers
- : Metainformasjon, her spesifiserer vi at vi sender JSONbody
- : Selve dataene, konvertert til JSON-streng med JSON.stringify()`
Her er et komplett eksempel som henter brukerdata fra et åpent API og viser det på nettsiden:
HTML:
``html`Brukeroversikt
JavaScript:
`javascript
const knapp = document.querySelector("#lastInn");
const statusDiv = document.querySelector("#status");
const listeDiv = document.querySelector("#brukerliste");
knapp.addEventListener("click", async function () {
statusDiv.textContent = "Laster...";
listeDiv.innerHTML = "";
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
if (!response.ok) {
throw new Error("Serverfeil: " + response.status);
}
const brukere = await response.json();
statusDiv.textContent = brukere.length + " brukere funnet:";
brukere.forEach(bruker => {
const kort = document.createElement("div");
kort.classList.add("bruker-kort");
kort.innerHTML =
E-post: ${bruker.email}
By: ${bruker.address.city}
;
listeDiv.appendChild(kort);
}); } catch (error) {
statusDiv.textContent = "Feil: " + error.message;
statusDiv.style.color = "red";
}
});
``Dette eksempelet kombinerer alt vi har lært: hendelseshåndtering (klikk-lytter), asynkron programmering (async/await med fetch), feilhåndtering (try/catch), JSON-parsing og DOM-manipulering (createElement, innerHTML, appendChild).
Når du henter data fra et annet domene enn ditt eget, gjelder CORS (Cross-Origin Resource Sharing)-regler. Nettleseren blokkerer forespørsler til andre domener med mindre serveren eksplisitt tillater det.
Hvis du får en CORS-feil i konsollen, betyr det at API-et ikke tillater forespørsler fra din nettside. Løsninger:
- Bruk et API som støtter CORS (de fleste åpne API-er gjør det)
- Bruk en proxy-server som videresender forespørslene
- Konfigurer serveren til å sende riktige CORS-headere
CORS er en sikkerhetsfunksjon som beskytter brukere mot at ondsinnede nettsider sender forespørsler på deres vegne.
Her er noen gratis API-er du kan bruke for å øve:
- JSONPlaceholder (jsonplaceholder.typicode.com): Falske data for testing (brukere, innlegg, kommentarer)
- Open-Meteo (api.open-meteo.com): Værdata for hele verden
- Rest Countries (restcountries.com/v3.1): Informasjon om verdens land
- PokeAPI (pokeapi.co/api/v2): Data om Pokémon
- Dog API (dog.ceo/api): Tilfeldige hundebilder
Alle disse API-ene er gratis, krever ingen autentisering og støtter CORS.
Hva betyr det at JavaScript er asynkront?
Hva er et API i websammenheng?
Hva skrives ut av denne koden?
``javascript``
console.log("A");
setTimeout(() => console.log("B"), 0);
console.log("C");
Hva gjør response.json() i en fetch()-forespørsel?
Skriv en asynkron funksjon hentLand(navn) som bruker fetch() og async/await til å hente informasjon om et land fra API-et https://restcountries.com/v3.1/name/{navn}. Funksjonen skal skrive ut landets offisielle navn og befolkningstall til konsollen. Inkluder feilhåndtering med try/catch.
Lag en komplett nettside-funksjon som henter en liste med brukere fra https://jsonplaceholder.typicode.com/users og viser dem som kort i en . Hvert kort skal vise brukerens navn, e-post og firma. Vis en "Laster..."-melding mens data hentes, og en feilmelding hvis noe går galt. Bruk async/await, DOM-manipulering og feilhåndtering.
Hvorfor kaster ikke fetch() en feil for HTTP-statuskode 404 (Ikke funnet)?
Skriv en async funksjon sendTilbakemelding(navn, melding) som sender en POST-forespørsel med JSON-data til https://jsonplaceholder.typicode.com/posts. Dataene skal inneholde title (navnet), body (meldingen) og userId satt til 1. Funksjonen skal returnere det opprettede objektet fra serveren, eller null ved feil. Inkluder korrekte headers og feilhåndtering.