Jag tänkte att här vore det ypperligt att använda async/await ...

Async/await Promise

Något som blir mer och mer populärt i JavaScript är att asynkrona funktioner och "Promise" (framtida löfte). I EcmaScript 2015 (JavaScript standard, utgåva 6, år 2015) standardiserades Promise, och blev inbyggt i JavaScript.
I EcmaScript 2017 (JavaScript standard, utgåva 8, år 2017) fick vi även async/await - som kan pausa och vänta-in (await) en Promise.

Tidigare i veckan behövde jag en funktion som:

  1. Hämtar alla revisioner av en fil
  2. Öppnar varje revision
  3. Räknar antal rader.

Först gjorde jag det genom att bara använda (callback) funktioner:

Men så kom jag på att här vore det ypperligt att använda async/await:

Dessa fyra rader gör ungefär samma sak som de elva raderna med (callback) funktionerna ovanför.
Det gör inget om du inte förstår vad som händer, då jag själv knappt förstår vad koden gör 😊

Array.prototype.map går igenom varje element i en array. Och kallar(callback) funktionen för varje element, och byter ut värdet för varje post, mot det som (callback) funktionen returnerar.

I detta fall är (callback) funktionen en anonym async pil-funktion. Eftersom den har nyckelordet async kan den med nyckelordet await vänta in resultatet från funktionen fileContent, som returnerar en Promise.

Det nuvarande värdet (revisions-object) i arrayen revisions, byts ut mot objektet {rev, loc} som returneras av Pil-funktionen.
Parenteserna behövs då anonyma pil-funktioner kan ha {} runt funktionens kropp.

Anonyma pil-funktioner

Ett av många syntax-godis som introducerades i EcmaScript 2015 är "Arrow functions". Vilket är en förkortning av anonyma funktioner, även kallat lamda-funktioner, och används flitigt tillsammans med Promise och async/await

function(foo) { return => foo + 1}

Man tar alltså bort function och return och gör en => pil i stället.

Fördelen med anonyma pilar är att det blir mindre syntax att skriva.
Mindre kod för samma jobb gör det roligare att programmera!

Nackdelarna med anonyma pilar är många: funktionen får ej något namn, det blir svårt att kopiera funktionen, koden blir ofta otydlig. Man kan göra riktig kluriga, nestade funktioner. Och då vi utvecklare är lata i allmänhet skriver vi hellre pilar än funktioner, så det blir anonyma pil-funktioner överallt, där det skulle vara bättre med riktiga funktioner med namn. Anonyma pilar bjuder även in till att använda en-bokstav-variabler, x, y, z i stället för riktiga namn.

Promise

Async/await bygger på att alla de asynkrona funktioner man vill vänta-in (await) returnerar en Promise.

Om allt går bra kallar man resolve() och om det blir något fel kallar man reject().

Alla Promise har metoderna .then() och .catch()

En Promise kan ha tre olika lägen:

Det går dock inte att kolla vilket läge/status en Promise befinner sig i.

Generator-funktioner och korutiner

I EcmaScript 2015 fick vi även generator-funktioner, och med dem kan man göra så kallade korutoiner.
Generator-funktioner gör det möjligt att pausa en funktion med nyckelordet yeld och fortsätta funktionen med motoden .next()

Man skapar en generator-funktion med nyckel-ordet/tecknet * alltså function* i stället för function eller async function.

Möjligheten att pausa och fortsätta gör det möjligt att skapa en korutin som fungerar nästan exakt som async/await. Exempel:

Async await

co(function*async function () {
  var user = yieldawait getUser();
  var comments = yieldawait getComments(user);
});

Async/await ser nästen exakt ut som en korutin.
Många utvecklare frågar sig om det verkligen är nödvändigt att använda async/await, och om du frågar mig så tycker jag personligen att det mesta som introducerades med EcmaScript åtgåva 6 och 8 inte var nödvändigt :P

Fördelar/nackdelar med async/await och Promises

En av många fördelar med Promise är att man (med hjälp av async/await) kan skriva async kod så att den ser ut att vara synkroniserad.

Nackdelarna är att det inte går att avbryta en Promise, eller få progress-information, till exempel hur mycket av filen som är inläst. Det blir krångligt om man vill detalj-styra flöden och hur olika fel ska hanteras. Och man måste skicka med värden i return så de kommer vidare i .then() kedjan.
Personligen tycker jag att Promise-baserad kod är svår att läsa, speciellt när det blir långa .then() -kedjor.

Med async/await och Promise är det lätt hänt att man glömmer hantera eventuella fel.
Koden blir mycket renare med async/await, men om man ska ha try{} catch{} överallt blir det fult igen. :P
Det är vanligt att ha en .catch() i slutet av Primise .then() kedjan och inte hantera varje eventuellt fel var för sig.

Promise passar bra när det inte är viktigt i vilken ordning sakar utförs, eller hur mycket som körs samtidigt. Och där man inte behöver detaljstyra olika flöden och fel-hantering.

När det ej är optimalt att använda async/await och Promise:



Blogginlägg skrivet av Johan Zetterberg 8 Maj 2018


Följ mig via RSS:   RSS https://zäta.com/rss.xml (ange adressen i din feed-läsare)