Hoppa till innehåll

Utmaningar

Nedan finns nio frågor om JSON, promises, fetch() och CORS. Frågorna bygger på veckans artiklar HTTP och JSON, Promises och async/await och Nätverksanrop med fetch.

Varje fråga har ett antal alternativ att välja bland. Klicka på “Visa lösning” för att se svaret och förklaringen.

Om du är osäker kan du alltid testa i webbläsarens devtools-konsol eller ta upp frågan på en handledning.

{
"title": 'Röda rummet',
"year": 1879,
}
  1. Inget, texten är giltig JSON.
  2. Strängen har enkla citattecken.
  3. Det finns ett kommatecken efter sista egenskapen.
  4. Både 2 och 3.

Svaret är alternativ 4. I JSON skrivs alla strängar och nycklar med dubbla citattecken, och det får inte finnas något kommatecken efter sista elementet. Båda är tillåtna i ett objekt i JavaScript, vilket gör felen lätta att missa. JSON.parse() kastar ett SyntaxError för texten.

JSON.stringify({ a: 1, b: undefined, c: () => 2 })
  1. '{"a":1,"b":undefined,"c":"() => 2"}'
  2. '{"a":1}'
  3. '{"a":1,"b":null,"c":null}'
  4. Ett TypeError kastas.

Svaret är alternativ 2, '{"a":1}'. JSON saknar undefined och funktioner, så JSON.stringify() hoppar över egenskaper med sådana värden utan att kasta något fel. I en array blir de i stället null, eftersom positionerna måste finnas kvar: JSON.stringify([1, undefined]) ger '[1,null]'.

Fråga 12-03: I vilken ordning skrivs siffrorna ut?

Section titled “Fråga 12-03: I vilken ordning skrivs siffrorna ut?”
console.log('1')
setTimeout(() => console.log('2'), 0)
Promise.resolve().then(() => console.log('3'))
async function run() {
console.log('4')
await null
console.log('5')
}
run()
console.log('6')
  1. 1, 2, 3, 4, 5, 6
  2. 1, 4, 6, 3, 5, 2
  3. 1, 4, 6, 2, 3, 5
  4. 1, 6, 4, 3, 5, 2

Svaret är alternativ 2.

Skriptet körs först klart: 1, sedan 4, eftersom en async-funktion körs synkront fram till sitt första await, och sist 6. Under tiden har två microtasks hamnat i kön: funktionen i then() och resten av run() efter await. De körs i den ordningen, 3 och 5, så snart skriptet är klart. Timern är en task och får vänta tills alla microtasks är körda, så 2 kommer sist.

async function getBook(id) {
const response = await fetch(`https://api.example.com/books/${id}`)
return response.json()
}
const result = getBook(42)
  1. Boken, som ett objekt.
  2. undefined, eftersom svaret inte har kommit än.
  3. Ett promise som är pending.
  4. Ett Response-objekt.

Svaret är alternativ 3. En async-funktion returnerar alltid ett promise, direkt när den anropas. Promiset blir fulfilled med boken först när svaret har kommit och tolkats. För att komma åt boken behövs await getBook(42) eller getBook(42).then(...).

Fråga 12-05: Vad händer när servern svarar 500?

Section titled “Fråga 12-05: Vad händer när servern svarar 500?”
try {
const response = await fetch('https://api.example.com/books')
console.log(response.status)
} catch {
console.log('Error')
}
  1. Error skrivs ut, eftersom fetch() kastar ett fel.
  2. 500 skrivs ut.
  3. Ett TypeError kastas, men fångas inte.
  4. Ingenting skrivs ut.

Svaret är alternativ 2, 500. Ett svar med en felkod är fortfarande ett svar, så promiset från fetch() blir fulfilled och catch körs inte. Promiset blir rejected bara när inget svar kan läsas: vid nätverksfel, när CORS stoppar förfrågan eller när den avbryts. Därför måste du själv kontrollera response.ok och kasta ett fel.

function wait(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds))
}
await Promise.all([wait(1000), wait(2000), wait(500)])

Ungefär hur lång tid tar det innan raden med Promise.all() är klar?

  1. 0,5 sekunder
  2. 1 sekund
  3. 2 sekunder
  4. 3,5 sekunder

Svaret är alternativ 3, 2 sekunder. Alla tre timrarna startar direkt när wait() anropas och räknar samtidigt. Promise.all() blir fulfilled när den sista är klar, alltså efter den längsta tiden. Hade de tre väntat med await en i taget hade det tagit 3,5 sekunder. Med Promise.race() eller Promise.any() hade det tagit 0,5 sekunder.

const result = await Promise.all([
Promise.resolve(1),
Promise.reject(new Error('Failed')),
Promise.resolve(3)
])
  1. [1, undefined, 3]
  2. [1, Error, 3]
  3. [1, 3]
  4. Inget resultat. Raden kastar felet Failed.

Svaret är alternativ 4. Promise.all() blir rejected så fort ett av promisen misslyckas, med just det felet, och await kastar det. Vill du ha resultatet av alla använder du Promise.allSettled(), som ger en array med { status: 'fulfilled', value } eller { status: 'rejected', reason } för vart och ett.

Fråga 12-08: Vilken URL har samma origin?

Section titled “Fråga 12-08: Vilken URL har samma origin?”

Sidan körs på https://app.example.com. Vilken av URL:erna har samma origin?

  1. https://app.example.com:443/api/books
  2. http://app.example.com/api/books
  3. https://api.example.com/books
  4. https://example.com/api/books

Svaret är alternativ 1. En origin består av protokoll, värdnamn och port. https har port 443 som standard, så https://app.example.com och https://app.example.com:443 är samma origin, trots att de är skrivna olika. Alternativ 2 har ett annat protokoll, och alternativ 3 och 4 har andra värdnamn. Att de delar domänen example.com spelar ingen roll.

Fråga 12-09: Vilken förfrågan föregås av en preflight-förfrågan?

Section titled “Fråga 12-09: Vilken förfrågan föregås av en preflight-förfrågan?”

Alla förfrågningar görs med fetch() till en annan origin än sidans egen.

  1. En GET utan egna headrar.
  2. En POST med Content-Type: application/x-www-form-urlencoded.
  3. En POST med Content-Type: application/json.
  4. Inget av dem; webbläsaren skickar alltid en preflight-förfrågan.

Svaret är alternativ 3. En GET utan egna headrar och en POST med en body av den sort ett HTML-formulär kan skicka är simple requests, och skickas direkt. application/json kan ett formulär inte skicka, så webbläsaren frågar först servern med en OPTIONS-förfrågan om förfrågan är tillåten. Det är därför en POST med JSON till B2:s API syns som två rader i fliken Network.

CCBY