Fetch ja asynkroniset funktiot
Javascriptin normaalit funktiot suoritetaan synkronisesti eli peräkkäin: seuraava rivi alkaa vasta kun edellinen on valmis. Verkkopyyntö ei voi toimia näin, koska palvelimen vastausta voi joutua odottamaan sekunteja. Jos selain jäisi odottamaan, koko sivu jäätyisi siksi aikaa.
Siksi verkkopyynnöt tehdään asynkronisesti fetch-rajapinnalla: koodi käynnistää pyynnön ja jatkaa eteenpäin, ja vastaukseen reagoidaan vasta kun se saapuu.
Asynkroninen ei tarkoita rinnakkaista
Tämä kannattaa ymmärtää heti alussa, koska väärä mielikuva aiheuttaa myöhemmin sekaannusta. Javascript on yksisäikeinen. Asynkroninen funktio ei suoriudu rinnakkain muun koodin kanssa — sen suoritus keskeytyy odotuskohdassa ja jatkuu myöhemmin siitä mihin se jäi.
Rinnakkain tapahtuu vain odottaminen: verkkopyynnön hoitaa selain, ei javascript-koodi. Tästä seuraa kaksi asiaa:
- Raskas laskenta jäädyttää sivun myös asynkronisessa funktiossa. Asynkronisuus ei auta siihen.
- Useita pyyntöjä voi silti olla menossa yhtä aikaa. Siihen palataan luvussa Rinnakkaiset pyynnöt.
Promise
Asynkroniset funktiot palauttavat Promise-objektin eli lupauksen siitä, että tulos saadaan joskus. Lupaus on aina yhdessä kolmesta tilasta:
| Tila | Merkitys | Fetch-pyynnössä |
|---|---|---|
| pending | tekeillä | pyyntö on lähetetty, vastausta odotetaan |
| fulfilled | onnistui | palvelin vastasi |
| rejected | epäonnistui | verkkovirhe tai yhteyttä ei saatu |
Lupauksia käsitellään kahdella tavalla: ketjuttamalla .then()-kutsuja tai käyttämällä async- ja await-avainsanoja. Käytä async/await-tapaa; se on luettavampi ja sen virheenkäsittely on tavallinen try-lohko. Ketjutettu muoto on hyvä tuntea, koska sitä näkee paljon.
Kaksi sääntöä, jotka selittävät lähes kaiken:
- async-funktio palauttaa aina Promisen, myös silloin kun return-lauseessa on tavallinen arvo.
- await purkaa lupauksen ja antaa sen sisällä olevan arvon. Sitä voi käyttää async-funktion sisällä tai moduulin ylätasolla.
Yksinkertaisin esimerkki
Esimerkeissä käytetään JSONPlaceholder-palvelua, joka tarjoaa testidataa ja sallii pyynnöt mistä tahansa osoitteesta. Kokeile esimerkkejä selaimen konsolissa.
async function haeKayttajat() {
// await odottaa, että palvelin vastaa
const vastaus = await fetch("https://jsonplaceholder.typicode.com/users");
// vastauksen runko luetaan erikseen, tästä lisää alempana
const kayttajat = await vastaus.json();
console.log(kayttajat);
}
haeKayttajat();
Sama ketjutettuna. Toiminta on identtinen, mutta rakenne on hankalampi lukea:
fetch("https://jsonplaceholder.typicode.com/users")
.then(vastaus => vastaus.json())
.then(kayttajat => console.log(kayttajat));
Miksi vastaus luetaan kahdessa vaiheessa?
Ensimmäinen await odottaa, että palvelimelta saapuvat otsakkeet — tilakoodi, sisältötyyppi ja muut tiedot. Runko voi olla megatavuja ja se saapuu virtana sen jälkeen, joten se luetaan erikseen omalla kutsullaan.
Siksi fetch palauttaa Response-objektin, ja rungon lukevat metodit (json(), text()) palauttavat kukin oman lupauksensa. Tämä on myös syy siihen, että await-sanoja tarvitaan kaksi.
Fetchin vaiheet
Seuraava kaavio kokoaa yhteen sen, mitä yhden fetch-kutsun aikana tapahtuu. Aika kulkee ylhäältä alas. Yhtenäiset nuolet ovat varsinaista liikennettä, katkoviivat lupauksia, jotka palautuvat javascript-koodille.
Kaaviosta näkyy kolme asiaa, jotka selittävät fetchin käyttäytymisen:
- Lupaus palautuu heti, kauan ennen kuin palvelin on vastannut mitään. Siksi koodin suoritus jatkuu eteenpäin ja seuraavat rivit ehtivät ajoon ennen dataa.
- Ensimmäinen await odottaa vain otsakkeita. Siinä kohtaa tiedetään jo tilakoodi, joten vastaus.ok tarkistetaan juuri tässä — ennen kuin runkoa aletaan lukea.
- Runko haetaan erikseen. Se voi olla iso ja se saapuu virtana, joten json() tai text() palauttaa oman lupauksensa ja tarvitsee toisen await-sanan.
Huomaa myös, mitä kaaviossa ei ole: mikään nuoli ei kerro virheestä. Palvelimen vastaus 404 kulkee kaaviossa täsmälleen samaa reittiä kuin onnistunut vastaus, ja lupaus täyttyy normaalisti. Vain katkennut yhteys estää nuolen palaamasta — vasta silloin lupaus hylätään. Tästä lisää luvussa Virheenkäsittely.
Suoritusjärjestys
Aja tämä konsolissa ja katso, missä järjestyksessä rivit tulostuvat:
console.log("1. ennen pyyntöä");
fetch("https://jsonplaceholder.typicode.com/users/1")
.then(vastaus => vastaus.json())
.then(kayttaja => console.log("3. data saapui:", kayttaja.name));
console.log("2. heti pyynnön jälkeen");
1. ennen pyyntöä
2. heti pyynnön jälkeen
3. data saapui: Leanne Graham
Rivi 2 tulostuu ennen rivia 3, vaikka se on koodissa myöhemmin. Pyyntö käynnistetään, suoritus jatkuu heti eteenpäin, ja vasta kun vastaus saapuu, käsittelijä pääsee vuoroon.
Sama pätee async-funktioon: haeKayttajat() palaa heti kutsujalle, ja funktio jatkaa taustalla.
console.log("ennen");
haeKayttajat(); // ei odota valmistumista
console.log("jälkeen"); // tämä tulostuu ennen haun tulosta
Huomaa, että Firefoxin konsoli merkitsee fetch-pyynnöt tunnuksella XHR GET. Nimi tulee vanhasta XMLHttpRequest-rajapinnasta, jota fetch on korvaamassa.
Virheenkäsittely
Tämä on fetchin tärkein yksittäinen asia, ja se yllättää lähes kaikki:
Fetch ei hylkää lupausta HTTP-virhekoodeista. Vastaus 404 tai 500 tuottaa täysin onnistuneen lupauksen. Vain verkkovirhe — yhteys katkeaa, palvelinta ei löydy, CORS estää pyynnön — hylkää lupauksen.
Tilanne on siis tarkistettava itse vastaus.ok-ominaisuudesta, joka on true vain kun tilakoodi on välillä 200–299:
async function haeKayttaja(id) {
try {
const vastaus = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
// ILMAN TÄTÄ 404-virhesivu päätyisi dataksi asti
if (!vastaus.ok) {
throw new Error(`HTTP-virhe ${vastaus.status} ${vastaus.statusText}`);
}
return await vastaus.json();
}
catch (virhe) {
console.error("Haku epäonnistui:", virhe.message);
return null;
}
}
// kokeile molemmat: id 1 on olemassa, id 9999 ei
console.log(await haeKayttaja(1));
console.log(await haeKayttaja(9999));
try-lohko nappaa kolme eri asiaa: verkkovirheen, itse heitetyn HTTP-virheen ja json()-metodin heittämän jäsennysvirheen. Viimeinen tulee vastaan juuri silloin kun palvelin palauttaa virhesivun HTML-muodossa eikä JSONia.
Jos käytät ketjutettua muotoa, virheet napataan .catch()-metodilla:
fetch(osoite)
.then(vastaus => {
if (!vastaus.ok) {
throw new Error(`HTTP-virhe ${vastaus.status}`);
}
return vastaus.json();
})
.then(data => console.log(data))
.catch(virhe => console.error(virhe.message));
Muista virheenkäsittely myös kutsuttaessa. Pelkkä
haeKayttaja(1); ilman await- tai
.catch()-käsittelyä tuottaa käsittelemättömän lupaushylkäyksen, joka
näkyy konsolissa mutta jää ohjelmalta huomaamatta.
Datan palauttaminen funktiosta
Tavallinen kompastuskivi: funktio hakee datan, mutta kutsuja ei saa sitä käsiinsä.
// EI TOIMI: data jää funktion sisään
async function hae() {
const vastaus = await fetch(osoite);
const data = await vastaus.json();
// ei return-lausetta
}
const tulos = hae();
console.log(tulos); // Promise { <pending> } - ei dataa
Koska async-funktio palauttaa aina Promisen, myös paluuarvo on purettava:
async function hae() {
const vastaus = await fetch(osoite);
return await vastaus.json(); // data palautetaan
}
// async-funktiossa tai moduulin ylätasolla:
const data = await hae();
// muualla:
hae().then(data => console.log(data));
JSON ja XML
Response-objektilla on useita metodeja rungon lukemiseen. Tärkeimmät ovat json(), text() ja blob(). Kutakin voi kutsua vain kerran: runko on virta, joka luetaan läpi yhden kerran.
JSON
json() lukee rungon ja jäsentää sen suoraan javascript-objektiksi tai -taulukoksi. Tämä on se muoto, jota viikkotehtävissä käytetään.
const vastaus = await fetch("https://jsonplaceholder.typicode.com/posts?userId=1");
const postaukset = await vastaus.json();
console.log(postaukset.length); // 10
console.log(postaukset[0].title);
for (const postaus of postaukset) {
console.log(`${postaus.id}: ${postaus.title}`);
}
json() heittää poikkeuksen, jos runko ei ole kelvollista JSONia. Siksi se kuuluu try-lohkon sisään.
XML
XML-muotoiselle datalle ei ole omaa metodia. Runko luetaan tekstinä text()-metodilla ja jäsennetään DOMParser-luokalla. Tuloksena on tavallinen Document, jota käsitellään samoilla DOM-metodeilla kuin www-sivua — querySelector, getAttribute, textContent ja niin edelleen.
Esimerkkinä käytetään Wikipedian rajapintaa, joka osaa palauttaa hakutulokset XML-muodossa ja on vapaasti käytettävissä. Rajapinta sallii pyynnöt mistä tahansa lähteestä, kun osoitteeseen lisätään parametri origin=* — tästä lisää luvussa CORS javascript-ohjelmoijan näkökulmasta.
Osoitteen kyselyosa kannattaa koota URLSearchParams-objektilla, joka hoitaa merkkien koodauksen puolestasi:
async function haeXml(osoite) {
const vastaus = await fetch(osoite);
if (!vastaus.ok) {
throw new Error(`HTTP-virhe ${vastaus.status}`);
}
const teksti = await vastaus.text();
const jasennin = new DOMParser();
const xml = jasennin.parseFromString(teksti, "application/xml");
// DOMParser EI heitä poikkeusta virheellisestä XML:stä, vaan palauttaa
// dokumentin, jossa on parsererror-elementti. Tämä on tarkistettava itse.
const virhe = xml.querySelector("parsererror");
if (virhe !== null) {
throw new Error(`Virheellinen XML: ${virhe.textContent}`);
}
return xml;
}
const parametrit = new URLSearchParams({
action: "query",
list: "search",
srsearch: "javascript",
srlimit: "5",
format: "xml",
origin: "*" // sallii pyynnön mistä tahansa lähteestä
});
const xml = await haeXml(`https://fi.wikipedia.org/w/api.php?${parametrit}`);
Vastaus on jotakin tällaista:
<?xml version="1.0"?>
<api>
<query>
<search>
<p ns="0" title="JavaScript" size="24601" wordcount="3020" />
<p ns="0" title="ECMAScript" size="5312" wordcount="620" />
</search>
</query>
</api>
Tulokset luetaan tutuilla DOM-metodeilla. Huomaa, että elementin nimi on p — se on MediaWikin oma nimi hakutulokselle eikä sillä ole mitään tekemistä HTML:n kappale-elementin kanssa:
for (const osuma of xml.querySelectorAll("search p")) {
const otsikko = osuma.getAttribute("title");
const sanoja = osuma.getAttribute("wordcount");
console.log(`${otsikko} (${sanoja} sanaa)`);
}
Toisena parametrina annettava tyyppi kannattaa olla "application/xml". Arvo "text/html" jäsentää sisällön HTML-sääntöjen mukaan, jolloin muotovirheet korjaillaan hiljaisesti eikä parsererror-elementtiä synny.
Sama toimii tietysti myös omalla palvelimella olevaan XML-tiedostoon. Silloin osoite on suhteellinen eikä CORSia tarvita lainkaan:
const xml = await haeXml("kayttajat.xml");
Rinnakkaiset pyynnöt
Tässä sana "rinnakkain" on paikallaan: useita pyyntöjä voi olla matkalla yhtä aikaa, vaikka javascript-koodi ajetaankin yhdessä säikeessä.
Varo await-sanaa silmukassa. Se odottaa jokaisen pyynnön valmistumista ennen seuraavan aloittamista, jolloin kymmenen pyyntöä kestää kymmenkertaisen ajan:
// HIDAS: pyynnöt tehdään peräkkäin
const postaukset = [];
for (const id of [1, 2, 3, 4, 5]) {
const vastaus = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
postaukset.push(await vastaus.json());
}
Promise.all käynnistää kaikki pyynnöt heti ja odottaa, kunnes viimeinenkin on valmis:
async function haeJson(osoite) {
const vastaus = await fetch(osoite);
if (!vastaus.ok) {
throw new Error(`HTTP-virhe ${vastaus.status}`);
}
return await vastaus.json();
}
// NOPEA: kaikki pyynnöt yhtä aikaa
const postaukset = await Promise.all(
[1, 2, 3, 4, 5].map(id => haeJson(`https://jsonplaceholder.typicode.com/posts/${id}`))
);
console.log(postaukset.map(p => p.title));
Sama toimii myös eri osoitteille, kun tarvitaan useaa eri aineistoa:
const [kayttajat, postaukset] = await Promise.all([
haeJson("https://jsonplaceholder.typicode.com/users"),
haeJson("https://jsonplaceholder.typicode.com/posts")
]);
Huomaa, että Promise.all epäonnistuu kokonaan, jos yksikin pyyntö epäonnistuu. Jos haluat tulokset niiltä osin kuin ne onnistuivat, käytä Promise.allSettled-metodia, joka palauttaa jokaisesta lupauksesta tilatiedon ja arvon.
Lataustilan näyttäminen käyttäjälle
Käyttöliittymän kannalta tämä on olennainen osa asynkronisuutta: pyynnön aikana käyttäjä odottaa, eikä sivu saa näyttää siltä että se on jumissa. Lukitse painike ja kerro mitä tapahtuu.
async function nappiaKlikattu(e) {
const nappi = e.currentTarget;
const tila = document.getElementById("tila");
const lista = document.getElementById("kayttajat");
nappi.disabled = true; // estetään tuplaklikkaus
tila.textContent = "Haetaan…";
try {
const kayttajat = await haeJson("https://jsonplaceholder.typicode.com/users");
const fragmentti = document.createDocumentFragment();
for (const kayttaja of kayttajat) {
const rivi = document.createElement("li");
rivi.textContent = kayttaja.name;
fragmentti.append(rivi);
}
lista.replaceChildren(fragmentti);
tila.textContent = `${kayttajat.length} käyttäjää.`;
}
catch (virhe) {
tila.textContent = `Haku epäonnistui: ${virhe.message}`;
}
finally {
// finally suoritetaan aina, myös virhetilanteessa
nappi.disabled = false;
}
}
finally-lohko on tässä olennainen: ilman sitä painike jäisi lukituksi, jos haku epäonnistuu.
Aikakatkaisu
Fetchillä ei ole oletusaikakatkaisua. Jos palvelin ei vastaa, pyyntö voi jäädä roikkumaan pitkäksi aikaa. Aikaraja asetetaan AbortSignal.timeout()-metodilla:
try {
const vastaus = await fetch(osoite, { signal: AbortSignal.timeout(5000) });
const data = await vastaus.json();
}
catch (virhe) {
if (virhe.name === "TimeoutError") {
console.error("Palvelin ei vastannut viidessä sekunnissa");
}
else {
console.error(virhe.message);
}
}
Pyynnön voi myös perua itse AbortController-objektilla — esimerkiksi silloin kun käyttäjä kirjoittaa hakukenttään uuden merkin ja edellinen haku on vielä kesken:
let ohjain = null;
async function hae(hakusana) {
// perutaan mahdollinen aiempi haku
if (ohjain !== null) {
ohjain.abort();
}
ohjain = new AbortController();
const vastaus = await fetch(`${osoite}?q=${hakusana}`, { signal: ohjain.signal });
return await vastaus.json();
}
Peruttu pyyntö hylkää lupauksen virheellä, jonka name on "AbortError".
CORS javascript-ohjelmoijan näkökulmasta
Ennemmin tai myöhemmin fetch-pyyntö epäonnistuu, konsoliin ilmestyy punainen CORS-virheilmoitus eikä mikään koodimuutos tunnu auttavan. Tässä luvussa kerrotaan, mistä on kyse ja mitä asialle voi javascript-puolella tehdä. Palvelimen asetukset kuuluvat Web-palvelinohjelmoinnin kurssille TIES4080.
Same-origin policy
Selaimen perussääntö on, että javascript-koodi saa lukea vastauksia vain samasta lähteestä kuin mistä sivu itse on ladattu. Lähde muodostuu kolmesta osasta: protokolla, isäntänimi ja portti. Kaikkien kolmen on täsmättävä.
Englanninkielinen termi on origin, ja se esiintyy sekä virheilmoituksissa että otsakkeiden nimissä (Access-Control-Allow-Origin).
| Sivun osoite | Pyynnön kohde | Sama lähde? |
|---|---|---|
| https://users.jyu.fi/~tommi/ | https://users.jyu.fi/data.json | kyllä |
| https://users.jyu.fi/~tommi/ | http://users.jyu.fi/data.json | ei — eri protokolla |
| https://users.jyu.fi/~tommi/ | https://appro.mit.jyu.fi/data.json | ei — eri isäntä |
| https://users.jyu.fi/~tommi/ | https://users.jyu.fi:8080/data.json | ei — eri portti |
| file:///C:/tyo/sivu.html | mikä tahansa | ei — tiedosto-osoitteen lähde on null |
Viimeinen rivi on syy siihen, miksi kurssilla toistetaan, että sivu on avattava WWW-palvelimen kautta eikä kaksoisklikkaamalla tiedostoa. Tiedosto-osoitteelta tehty fetch epäonnistuu aina.
Mitä CORS tekee
CORS (Cross-Origin Resource Sharing) on tapa, jolla palvelin voi sallia poikkeuksen tähän sääntöön. Palvelin lähettää vastauksessa otsakkeen
Access-Control-Allow-Origin: *
jolloin selain päästää javascript-koodin lukemaan vastauksen. Juuri tämän takia JSONPlaceholder toimii esimerkeissä mistä tahansa: se lähettää tuon otsakkeen.
Olennaista on ymmärtää kuka päättää mitäkin:
- Palvelin päättää, kuka saa lukea sen vastauksia.
- Selain valvoo päätöstä ja estää lukemisen, jos lupaa ei ole.
- Javascript-koodi ei voi vaikuttaa asiaan mitenkään. Mikään fetchin asetus ei anna lupaa, jota palvelin ei ole antanut.
Huomaa myös, että pyyntö lähtee ja palvelin käsittelee sen — selain vain estää vastauksen lukemisen. CORS ei siis suojaa palvelinta vaan käyttäjää: se estää sivustoa lukemasta tietoja, joihin käyttäjä on kirjautunut toisaalla.
Esitarkistuspyyntö
Yksinkertaiset pyynnöt — GET ja tavallinen lomakemuotoinen POST — lähtevät suoraan. Jos pyynnössä on esimerkiksi Content-Type: application/json, omia otsakkeita tai metodi PUT tai DELETE, selain lähettää ensin automaattisesti OPTIONS-pyynnön ja kysyy luvan.
Tämä näkyy kehittäjätyökalujen Network-välilehdellä ylimääräisenä OPTIONS-rivinä. Jos se epäonnistuu, varsinaista pyyntöä ei koskaan lähetetä. Esitarkistusta ei tarvitse itse tehdä eikä siihen voi vaikuttaa.
Miltä CORS-virhe näyttää koodissa
Tämä yllättää: CORS-virhettä ei voi erottaa verkkovirheestä. Fetch hylkää lupauksen TypeError-virheellä, jonka viesti on yleensä "Failed to fetch" tai "NetworkError". Tilakoodia ei ole luettavissa, koska koodi ei saa lukea vastauksesta mitään:
try {
const vastaus = await fetch("https://jokumuu.example/data.json");
console.log(vastaus.status); // tänne ei päästä
}
catch (virhe) {
console.error(virhe.name, virhe.message); // TypeError Failed to fetch
}
Varsinainen selitys löytyy vain konsolista. Selain kirjoittaa sinne tarkan syyn — esimerkiksi että Access-Control-Allow-Origin-otsake puuttuu. Lue se ilmoitus; se on ainoa paikka, josta syy selviää.
Älä käytä no-cors-tilaa
Kun CORS-virhe tulee vastaan, hakukone ehdottaa asetusta mode: "no-cors". Se ei korjaa mitään. Pyyntö kyllä onnistuu, mutta vastauksesta tulee läpinäkymätön (opaque): tilakoodi on 0, otsakkeita ei näy, ja rungon lukeminen antaa tyhjän merkkijonon.
const vastaus = await fetch(osoite, { mode: "no-cors" });
console.log(vastaus.status); // 0
console.log(await vastaus.text()); // "" - tyhjä
Virheilmoitus katoaa mutta data ei tule perille, mikä on hankalampi vikatilanne kuin selkeä virhe.
Mitä voit oikeasti tehdä
- Käytä samaa lähdettä. Jos data on omalla palvelimellasi, viittaa siihen suhteellisella polulla ("data.json") äläkä täydellä osoitteella. Tämä ratkaisee ongelman kurssin viikkotehtävissä.
- Käytä rajapintaa, joka sallii CORSin. JSONPlaceholder ja useimmat julkiset rajapinnat lähettävät tarvittavat otsakkeet.
- Pyydä palvelimen ylläpitäjää lisäämään otsake. Tämä on ainoa oikea ratkaisu, jos data on toisella palvelimella ja sinun pitää päästä siihen käsiksi.
- Avaa sivu WWW-palvelimen kautta äläkä file://-osoitteella. Kurssilla tämä tarkoittaa users.jyu.fi-palvelinta tai VS Coden Live Server -laajennusta.
Palvelinpuolen CORS-asetuksiin ja niiden turvallisuusvaikutuksiin perehdytään kurssilla TIES4080 Web-palvelinohjelmointi.
Fetchin asetukset
Toisena parametrina annetaan asetusobjekti. Useimmiten tarvitaan vain method, headers ja body — loput ovat oletusarvoillaan järkeviä.
const uusiPostaus = {
title: "Otsikko",
body: "Sisältö",
userId: 1
};
const vastaus = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(uusiPostaus) // rungon on vastattava Content-Type-otsaketta
});
if (!vastaus.ok) {
throw new Error(`HTTP-virhe ${vastaus.status}`);
}
console.log(await vastaus.json()); // JSONPlaceholder palauttaa { ..., id: 101 }
JSONPlaceholder ei oikeasti tallenna mitään, mutta se vastaa kuten oikea rajapinta, joten POST-pyyntöä voi harjoitella turvallisesti.
Lomakedatan voi lähettää myös suoraan FormData-objektina. Silloin ei pidä asettaa Content-Type-otsaketta itse: selain asettaa sen ja tarvittavan erotinmerkinnän automaattisesti.
const vastaus = await fetch(osoite, {
method: "POST",
body: new FormData(lomake)
});
Muita asetuksia, joita voi joutua säätämään:
| Asetus | Oletus | Merkitys |
|---|---|---|
| method | "GET" | HTTP-metodi |
| headers | — | pyynnön otsakkeet |
| body | — | pyynnön runko; ei sallittu GET-pyynnössä |
| credentials | "same-origin" | lähetetäänkö evästeet mukana |
| cache | "default" | välimuistin käyttö |
| signal | — | pyynnön peruminen, katso Aikakatkaisu |
| mode | "cors" | älä muuta, katso no-cors |
credentials: "include" lähettää evästeet myös toiseen lähteeseen, mutta se vaatii palvelimelta erikseen luvan — Access-Control-Allow-Origin: * ei riitä, vaan lähde on nimettävä täsmällisesti.
Lisätietoa
- Using the Fetch API — MDN
- Response
- Using promises
- Promise
- async function ja await
- Cross-Origin Resource Sharing (CORS)
- DOMParser
- JSONPlaceholder — esimerkeissä käytetty testirajapinta
- Promises, async/await — javascript.info
Käyttäjien kommentit