Hvad det er, og hvordan man bruger det

Når du skriver JavaScript-applikationer, kan du være stødt på asynkrone funktioner, såsom hente-funktionen i browseren eller readFile-funktionen i Nodejs.

Du kan have fået uventede resultater, hvis du brugte en af ​​disse funktioner, som du normalt ville. Dette skyldes, at de er asynkrone funktioner. Denne artikel guider, hvad det betyder, og hvordan man bruger asynkrone funktioner som en professionel.

Introduktion til synkron funktion

JavaScript er et enkelt-trådet sprog, der kun kan én ting ad gangen. Det betyder, at hvis processoren støder på en funktion, der tager lang tid, venter JavaScript, indtil hele funktionen er udført, før den går videre til andre dele af programmet.

De fleste funktioner udføres udelukkende af processoren. Det betyder, at under udførelsen af ​​de nævnte funktioner, uanset hvor lang tid det tager, vil processoren være helt optaget. Disse kaldes synkrone funktioner. Et eksempel på en synkron funktion er defineret nedenfor:

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);

Denne funktion udfører en stor loop, hvis udførelse tager lang tid, før den returnerer summen af ​​de to argumenter.

Efter at have defineret funktionen, kaldte vi den og gemte dens resultat i sumvariablen. Dernæst loggede vi værdien af ​​sumvariablen. Selvom det tager et stykke tid at udføre tilføjelsesfunktionen, kan processoren ikke fortsætte med at logge summen, før eksekveringen er fuldført.

Langt de fleste funktioner, du vil støde på, vil opføre sig på forudsigelige måder som den ovenfor. Nogle funktioner er dog asynkrone og opfører sig ikke som almindelige funktioner.

Introduktion til asynkron funktion

Asynkrone funktioner udfører størstedelen af ​​deres arbejde uden for processoren. Dette betyder, at selvom funktionen kan tage et stykke tid at fuldføre udførelse, vil processoren være ledig og fri til at udføre mere arbejde.

Her er et eksempel på en sådan funktion:

fetch('https://jsonplaceholder.typicode.com/users/1');

For at øge effektiviteten gør JavaScript det muligt for processoren at gå videre til andre opgaver, der kræver CPU’en, selv før den asynkrone funktions udførelse er fuldført.

Da processoren gik videre, før den asynkrone funktions eksekvering var fuldført, vil dens resultat ikke være umiddelbart tilgængeligt. Det vil være afventende. Hvis processoren forsøgte at udføre andre dele af programmet, der var afhængige af det afventende resultat, ville vi få fejl.

Derfor bør processoren kun udføre dele af programmet, der ikke afhænger af det afventende resultat. For at gøre dette bruger moderne JavaScript løfter.

Hvad er et løfte i JavaScript?

I JavaScript er et løfte en midlertidig værdi, som en asynkron funktion returnerer. Løfter er rygraden i moderne asynkron programmering i JavaScript.

Efter et løfte er skabt, sker der én af to ting. Det løser enten, når den asynkrone funktions returværdi er produceret, eller afvises i tilfælde af en fejl. Disse er begivenheder inden for et løftes livscyklus. Derfor kan vi knytte hændelseshandlere til løftet om at blive kaldt, når det løser eller afviser.

Al den kode, der kræver den endelige værdi af en asynkron funktion, kan knyttes til løftets hændelseshandler, når det løses. Al den kode, der håndterer fejlen ved et afvist løfte, vil også blive knyttet til dens tilsvarende hændelseshandler.

Her er et eksempel, hvor vi læser data fra 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));

I den første linje importerer vi fs/promises modulet.

I den anden linje kalder vi readFile-funktionen, og sender navnet og kodningen for den fil, hvis indhold vi vil læse. Denne funktion er asynkron; derfor giver det et løfte. Vi gemmer løftet i fileReadPromise-variablen.

I tredje linje vedhæftede vi en begivenhedslytter for, hvornår løftet løser sig. Det gjorde vi ved at kalde den daværende metode på løfteobjektet. Som et argument for vores opfordring til den daværende metode, sendte vi funktionen til at køre, hvis og når løftet løser sig.

I fjerde linje vedhæftede vi en lytter til, hvornår løftet afvises. Dette gøres ved at kalde catch-metoden og indsætte fejlhændelseshandleren som et argument.

En alternativ tilgang er at bruge async og afvente søgeord. Vi vil dække denne tilgang næste gang.

Asynkron og afvent forklaret

Async og Await nøgleord kan bruges til at skrive asynkront Javascript på en måde, der ser bedre ud syntaktisk. I dette afsnit vil jeg forklare, hvordan du bruger søgeordene, og hvilken effekt de har på din kode.

Nøgleordet afvent bruges til at pause udførelsen af ​​en funktion, mens man venter på, at en asynkron funktion er færdig. Her er et eksempel:

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 brugte nøgleordet afvent, mens vi ringede til readFile. Dette instruerede processoren til at vente, indtil filen er blevet læst, før den næste linje (console.log) kan udføres. Dette er med til at sikre, at kode, der afhænger af en asynkron funktions resultat, ikke vil blive udført, før resultatet bliver tilgængeligt.

Hvis du prøvede at køre ovenstående kode, ville du støde på en fejl. Dette skyldes, at afvent kun kan bruges i en asynkron funktion. For at erklære en funktion som asynkron, bruger du nøgleordet async før funktionserklæringen som sådan:

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ører dette kodestykke, vil du se, at JavaScript udfører den ydre console.log, mens du venter på, at data læst fra tekstfilen bliver tilgængelige. Når den er tilgængelig, udføres console.log inde i readData.

Fejlhåndtering under brug af async and await nøgleord udføres normalt ved hjælp af prøv/fang blokke. Det er også vigtigt at vide, hvordan man sløjfe med asynkron kode.

Async og await er tilgængelige i moderne JavaScript. Traditionelt blev asynkron kode skrevet ved brug af tilbagekald.

Introduktion til tilbagekald

Et tilbagekald er en funktion, der vil blive kaldt, når resultatet er tilgængeligt. Al kode, der kræver returværdien, vil blive sat i tilbagekaldet. Alt andet uden for tilbagekaldet afhænger ikke af resultatet og er derfor gratis at udføre.

Her er et eksempel, der 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")

I den første linje importerede vi fs-modulet. Dernæst kaldte vi readFile-funktionen i fs-modulet. ReadFile-funktionen vil læse tekst fra en fil, vi angiver. Det første argument er, hvilken fil det er, og det andet angiver filformatet.

ReadFile-funktionen læser teksten fra filer asynkront. For at gøre dette tager den en funktion ind som et argument. Dette funktionsargument er en tilbagekaldsfunktion og vil blive kaldt, når dataene er blevet læst.

Det første argument, der sendes, når tilbagekaldsfunktionen kaldes, er en fejl, der vil have en værdi, hvis der opstår en fejl, mens funktionen kører. Hvis der ikke opstår nogen fejl, vil den være udefineret.

Det andet argument, der sendes til tilbagekaldet, er dataene, der er læst fra filen. Koden inde i denne funktion vil få adgang til dataene fra filen. Kode uden for denne funktion kræver ikke data fra filen; kan derfor udføres, mens du venter på data fra filen.

Kørsel af ovenstående kode vil give følgende resultat:

Nøgle JavaScript-funktioner

Der er nogle nøglefunktioner og karakteristika, der påvirker, hvordan asynkron JavaScript fungerer. De er godt forklaret i videoen nedenfor:

Jeg har kort skitseret de to vigtige funktioner nedenfor.

#1. Enkeltgevind

I modsætning til andre sprog, der tillader programmøren at bruge flere tråde, tillader JavaScript dig kun at bruge én tråd. En tråd er en sekvens af instruktioner, der logisk afhænger af hinanden. Flere tråde tillader programmet at udføre en anden tråd, når der opstår blokeringsoperationer.

Men flere tråde tilføjer kompleksitet og gør det sværere at forstå de programmer, der bruger dem. Dette gør det mere sandsynligt, at der vil blive introduceret fejl i koden, og det ville være svært at fejlsøge koden. JavaScript blev lavet enkelt-trådet for enkelhedens skyld. Som et enkelttrådet sprog er det afhængigt af at være hændelsesdrevet for at håndtere blokeringsoperationer effektivt.

#2. Hændelsesdrevet

JavaScript er også begivenhedsdrevet. Det betyder, at nogle hændelser sker i løbet af et JavaScript-programs livscyklus. Som programmør kan du knytte funktioner til disse hændelser, og når hændelsen sker, vil den vedhæftede funktion blive kaldt og udført.

Nogle hændelser kan skyldes, at resultatet af en blokeringsoperation er tilgængeligt. I dette tilfælde kaldes den tilhørende funktion så med resultatet.

Ting at overveje, når du skriver asynkron JavaScript

I dette sidste afsnit vil jeg nævne nogle ting, du skal overveje, når du skriver asynkron JavaScript. Dette vil omfatte browsersupport, bedste praksis og vigtighed.

Browser support

Dette er en tabel, der viser understøttelsen af ​​løfter i forskellige browsere.

Kilde: caniuse.com

Dette er en tabel, der viser understøttelsen af ​​asynkrone søgeord i forskellige browsere.

Kilde: caniuse.com

Bedste praksis

  • Vælg altid async/wait, da det hjælper dig med at skrive renere kode, der er let at tænke på.
  • Håndter fejl i try/catch-blokke.
  • Brug kun nøgleordet async, når det er nødvendigt at vente på resultatet af en funktion.

Vigtigheden af ​​asynkron kode

Asynkron kode giver dig mulighed for at skrive mere effektive programmer, der kun bruger én tråd. Dette er vigtigt, da JavaScript bruges til at bygge websteder, der laver mange asynkrone operationer, såsom netværksanmodninger og læsning eller skrivning af filer til disk. Denne effektivitet har gjort det muligt for runtimes som NodeJS at vokse i popularitet som den foretrukne runtime for applikationsservere.

Afsluttende ord

Dette har været en lang artikel, men i den var vi i stand til at dække, hvordan asynkrone funktioner adskiller sig fra almindelige synkrone funktioner. Vi dækkede også, hvordan man bruger asynkron kode ved at bruge løfter, async/wait nøgleord og tilbagekald.

Derudover dækkede vi nøglefunktionerne i JavaScript. I det sidste afsnit afsluttede vi med at dække browsersupport og bedste praksis.

Tjek derefter Node.js’ ofte stillede interviewspørgsmål.