När du skriver JavaScript-applikationer kan du ha stött på asynkrona funktioner, såsom hämtningsfunktionen i webbläsaren eller readFile-funktionen i Nodejs.
Du kan ha fått oväntade resultat om du använde någon av dessa funktioner som du normalt skulle göra. Detta beror på att de är asynkrona funktioner. Den här artikeln vägleder vad det betyder och hur man använder asynkrona funktioner som ett proffs.
Innehållsförteckning
Introduktion till Synchronous Function
JavaScript är ett entrådigt språk som bara kan göra en sak åt gången. Det betyder att om processorn stöter på en funktion som tar lång tid, väntar JavaScript tills hela funktionen har exekveras innan den går vidare till andra delar av programmet.
De flesta funktioner exekveras helt av processorn. Detta innebär att under utförandet av nämnda funktioner, oavsett hur lång tid det tar, kommer processorn att vara helt upptagen. Dessa kallas synkrona funktioner. Ett exempel på en synkron funktion har definierats nedan:
function add(a, b) { for (let i = 0; i < 1000000; i ++) { // Do nothing } return a + b; } // Calling the function will take a long time sum = add(10, 5); // However, the processor cannot move to the console.log(sum);
Denna funktion utför en stor loop vars exekvering tar lång tid innan summan av dess två argument returneras.
Efter att ha definierat funktionen kallade vi den och lagrade dess resultat i summavariabeln. Därefter loggade vi värdet på summavariabeln. Även om exekveringen av add-funktionen tar ett tag, kan processorn inte gå vidare för att logga summan förrän exekveringen är klar.
De allra flesta funktioner du kommer att stöta på kommer att bete sig på förutsägbara sätt som den ovan. Vissa funktioner är dock asynkrona och beter sig inte som vanliga funktioner.
Introduktion till asynkron funktion
Asynkrona funktioner gör det mesta av sitt arbete utanför processorn. Detta innebär att även om funktionen kan ta ett tag att slutföra exekveringen kommer processorn att vara ledig och fri att göra mer arbete.
Här är ett exempel på en sådan funktion:
fetch('https://jsonplaceholder.typicode.com/users/1');
För att öka effektiviteten gör JavaScript det möjligt för processorn att gå vidare till andra uppgifter som kräver CPU:n även innan den asynkrona funktionens exekvering är klar.
Eftersom processorn gick vidare innan den asynkrona funktionens exekvering var klar, kommer dess resultat inte att vara omedelbart tillgängligt. Det kommer att vänta. Om processorn försökte köra andra delar av programmet som berodde på det väntande resultatet skulle vi få fel.
Därför bör processorn endast köra delar av programmet som inte är beroende av det väntande resultatet. För att göra detta använder modern JavaScript löften.
Vad är ett löfte i JavaScript?
I JavaScript är ett löfte ett tillfälligt värde som en asynkron funktion returnerar. Löften är ryggraden i modern asynkron programmering i JavaScript.
Efter att ett löfte skapats händer en av två saker. Det löser sig antingen när den asynkrona funktionens returvärde produceras eller avvisas i händelse av ett fel. Dessa är händelser inom ett löftes livscykel. Därför kan vi koppla händelsehanterare till löftet att bli anropat när det löser sig eller avvisar.
All kod som kräver det slutliga värdet av en asynkron funktion kan kopplas till löftets händelsehanterare för när det löser sig. All kod som hanterar felet i ett avvisat löfte kommer också att kopplas till dess motsvarande händelsehanterare.
Här är ett exempel där vi läser data från en fil i Nodejs.
const fs = require('fs/promises'); fileReadPromise = fs.readFile('./hello.txt', 'utf-8'); fileReadPromise.then((data) => console.log(data)); fileReadPromise.catch((error) => console.log(error));
På första raden importerar vi modulen fs/promises.
På den andra raden anropar vi funktionen readFile och skickar in namnet och kodningen för filen vars innehåll vi vill läsa. Denna funktion är asynkron; därför ger det ett löfte. Vi lagrar löftet i variabeln fileReadPromise.
På den tredje raden bifogade vi en evenemangslyssnare för när löftet löser sig. Vi gjorde detta genom att anropa den dåvarande metoden på löftesobjektet. Som ett argument för vår uppmaning till den dåvarande metoden skickade vi in funktionen för att köra om och när löftet löser sig.
På fjärde raden bifogade vi en lyssnare för när löftet avvisas. Detta görs genom att anropa catch-metoden och skicka in felhändelsehanteraren som ett argument.
Ett alternativt tillvägagångssätt är att använda async och avvakta nyckelord. Vi kommer att ta upp detta tillvägagångssätt härnäst.
Async och Await Explained
Async och Await nyckelord kan användas för att skriva asynkront Javascript på ett sätt som ser bättre ut syntaktiskt. I det här avsnittet kommer jag att förklara hur du använder nyckelorden och vilken effekt de har på din kod.
Nyckelordet await används för att pausa exekveringen av en funktion medan man väntar på att en asynkron funktion ska slutföras. Här är ett exempel:
const fs = require('fs/promises'); function readData() { const data = await fs.readFile('./hello.txt', 'utf-8'); // This line will not be executed until the data becomes available console.log(data); } readData()
Vi använde nyckelordet await när vi ringde till readFile. Detta instruerade processorn att vänta tills filen har lästs innan nästa rad (console.log) kan köras. Detta hjälper till att säkerställa att kod som beror på en asynkron funktions resultat inte kommer att exekveras förrän resultatet blir tillgängligt.
Om du försökte köra ovanstående kod skulle du stöta på ett fel. Detta beror på att await endast kan användas i en asynkron funktion. För att deklarera en funktion som asynkron använder du nyckelordet async före funktionsdeklarationen så här:
const fs = require('fs/promises'); async function readData() { const data = await fs.readFile('./hello.txt', 'utf-8'); // This line will not be executed until the data becomes available console.log(data); } // Calling the function so it runs readData() // Code at this point will run while waiting for the readData function to complete console.log('Waiting for the data to complete')
När du kör det här kodavsnittet kommer du att se att JavaScript kör den yttre console.log medan du väntar på att data som läses från textfilen blir tillgänglig. När det är tillgängligt körs console.log inuti readData.
Felhantering vid användning av nyckelordet async and await utförs vanligtvis med hjälp av försök fånga block. Det är också viktigt att veta hur man gör slinga med asynkron kod.
Async och await är tillgängliga i modern JavaScript. Traditionellt skrevs asynkron kod med hjälp av callbacks.
Introduktion till återuppringningar
En återuppringning är en funktion som kommer att anropas när resultatet är tillgängligt. All kod som kräver returvärdet kommer att läggas in i återuppringningen. Allt annat utanför återuppringningen beror inte på resultatet och är därför gratis att utföra.
Här är ett exempel som läser en fil i Nodejs.
const fs = require("fs"); fs.readFile("./hello.txt", "utf-8", (err, data) => { // In this callback, we put all code that requires if (err) console.log(err); else console.log(data); }); // In this part here we can perform all the tasks that do not require the result console.log("Hello from the program")
På den första raden importerade vi fs-modulen. Därefter kallade vi readFile-funktionen för fs-modulen. ReadFile-funktionen kommer att läsa text från en fil som vi anger. Det första argumentet är vilken fil det är, och det andra anger filformatet.
ReadFile-funktionen läser texten från filer asynkront. För att göra detta tar den in en funktion som ett argument. Detta funktionsargument är en callback-funktion och kommer att anropas när data har lästs.
Det första argumentet som skickas när callback-funktionen anropas är ett fel som kommer att ha ett värde om ett fel uppstår medan funktionen körs. Om inget fel påträffas kommer det att vara odefinierat.
Det andra argumentet som skickas till återuppringningen är data som läses från filen. Koden i den här funktionen kommer åt data från filen. Kod utanför denna funktion kräver inte data från filen; kan därför köras medan du väntar på data från filen.
Att köra ovanstående kod skulle ge följande resultat:
Viktiga JavaScript-funktioner
Det finns några viktiga funktioner och egenskaper som påverkar hur asynkron JavaScript fungerar. De förklaras väl i videon nedan:
Jag har kortfattat beskrivit de två viktiga funktionerna nedan.
#1. Enkeltrådig
Till skillnad från andra språk som tillåter programmeraren att använda flera trådar, tillåter JavaScript dig bara att använda en tråd. En tråd är en sekvens av instruktioner som logiskt sett beror på varandra. Flera trådar tillåter programmet att köra en annan tråd när blockeringsoperationer påträffas.
Men flera trådar lägger till komplexitet och gör det svårare att förstå programmen som använder dem. Detta gör det mer sannolikt att buggar kommer att introduceras i koden, och det skulle vara svårt att felsöka koden. JavaScript gjordes entrådigt för enkelhetens skull. Som ett entrådigt språk förlitar det sig på att vara händelsestyrt för att hantera blockeringsoperationer effektivt.
#2. Händelsestyrd
JavaScript är också händelsestyrt. Detta innebär att vissa händelser inträffar under ett JavaScript-programs livscykel. Som programmerare kan du koppla funktioner till dessa händelser, och närhelst händelsen inträffar kommer den bifogade funktionen att anropas och exekveras.
Vissa händelser kan bero på att resultatet av en blockeringsoperation är tillgängligt. I detta fall anropas sedan den associerade funktionen med resultatet.
Saker att tänka på när du skriver asynkron JavaScript
I det här sista avsnittet kommer jag att nämna några saker att tänka på när du skriver asynkron JavaScript. Detta inkluderar webbläsarstöd, bästa praxis och vikt.
Webbläsarstöd
Detta är en tabell som visar stödet för löften i olika webbläsare.
Källa: caniuse.com
Det här är en tabell som visar stödet för asynkrona nyckelord i olika webbläsare.
Källa: caniuse.com
Bästa metoder
- Välj alltid async/await, eftersom det hjälper dig att skriva renare kod som är lätt att tänka på.
- Hantera fel i försök/fånga block.
- Använd nyckelordet async endast när det är nödvändigt att vänta på resultatet av en funktion.
Vikten av asynkron kod
Asynkron kod gör att du kan skriva mer effektiva program som bara använder en tråd. Detta är viktigt eftersom JavaScript används för att bygga webbplatser som gör många asynkrona operationer, såsom nätverksförfrågningar och läsning eller skrivning av filer till disk. Denna effektivitet har gjort det möjligt för körtider som NodeJS att växa i popularitet som den föredragna körtiden för applikationsservrar.
Slutord
Detta har varit en lång artikel, men i den kunde vi täcka hur asynkrona funktioner skiljer sig från vanliga synkrona funktioner. Vi täckte också hur man använder asynkron kod genom att använda bara löften, async/await nyckelord och callbacks.
Dessutom täckte vi de viktigaste funktionerna i JavaScript. I det sista avsnittet avslutade vi med webbläsarstöd och bästa praxis.
Kolla sedan in Node.js’ vanliga intervjufrågor.