Er wordt veel gepraat over asynchrone programmering, maar wat is precies het probleem? Het probleem is dat we willen dat onze code niet blokkeert.
Taken die onze applicatie kunnen blokkeren, zijn onder meer het maken van HTTP-aanvragen, het opvragen van een database of het openen van een bestand. Sommige talen, zoals Java, pakken dit aan door meerdere threads te maken. JavaScript heeft echter maar één thread, dus we moeten onze programma's zo ontwerpen dat geen enkele taak de stroom blokkeert.
Asynchrone programmering lost dit probleem op. Het laat ons taken later uitvoeren, zodat we niet het hele programma ophouden te wachten tot taken voltooid zijn. Het helpt ook als we willen dat taken opeenvolgend worden uitgevoerd.
In deel een van deze tutorial zullen we de concepten achter synchrone en asynchrone code leren en zien hoe we callback-functies kunnen gebruiken om problemen met asynchronie op te lossen.
Ik wil dat je je de laatste keer herinnert dat je ging winkelen in de supermarkt. Er waren waarschijnlijk meerdere kassa's open voor klanten. Dit helpt de winkel om meer transacties in dezelfde hoeveelheid tijd te verwerken. Dit is een voorbeeld van concurrency.
Simpel gezegd, concurrency doet meer dan één taak tegelijkertijd. Uw besturingssysteem is gelijktijdig omdat meerdere processen tegelijkertijd worden uitgevoerd. Een proces is een uitvoeringsomgeving of een exemplaar van een actieve toepassing. Uw browser, teksteditor en antivirussoftware zijn bijvoorbeeld alle processen op uw computer die gelijktijdig worden uitgevoerd.
Toepassingen kunnen ook gelijktijdig zijn. Dit wordt bereikt met threads. Ik zal niet te diep gaan omdat dit verder gaat dan de reikwijdte van dit artikel. Als je een diepgaande uitleg wilt over hoe JavaScript werkt onder de motorkap, raad ik aan deze video te bekijken.
Een thread is een eenheid binnen een proces dat code uitvoert. In ons winkelvoorbeeld zou elke kassa een rode draad zijn. Als we maar één uitchecklijn in de winkel hebben, zou dat de manier veranderen waarop we klanten verwerken.
Ben je ooit in de rij geweest en had iets je transactie in de hand? Misschien had je een prijscontrole nodig of moest je een manager zien. Wanneer ik op het postkantoor een pakket probeer te verzenden en ik heb mijn etiketten niet ingevuld, vraagt de kassier me om opzij te gaan terwijl ze andere klanten blijven controleren. Als ik klaar ben, ga ik terug naar de voorkant van de lijn om te worden uitgecheckt.
Dit is vergelijkbaar met hoe asynchroon programmeren werkt. De kassier had op me kunnen wachten. Soms doen ze dat. Maar het is een betere ervaring om niet in de rij te staan en de andere klanten te bekijken. Het punt is dat klanten niet moeten worden uitgecheckt in de volgorde waarin ze in de rij staan. Evenzo hoeft code niet te worden uitgevoerd in de volgorde waarin we het schrijven.
Het is normaal om te denken aan onze code die sequentieel van boven naar beneden wordt uitgevoerd. Dit is synchroon. Bij JavaScript zijn sommige taken echter inherent asynchroon (bijvoorbeeld setTimeout) en sommige taken ontwerpen we asynchroon omdat we weten dat ze van te voren geblokkeerd kunnen worden.
Laten we een praktisch voorbeeld bekijken met behulp van bestanden in Node.js. Als u de codevoorbeelden wilt uitproberen en een inleiding nodig hebt over het gebruik van Node.js, kunt u instructies voor het starten van deze zelfstudie vinden. In dit voorbeeld openen we een bestand met berichten en halen we een van de berichten op. Vervolgens openen we een bestand met opmerkingen en halen we de opmerkingen voor dat bericht op.
Dit is de synchrone manier:
const fs = require ('fs'); const path = require ('path'); const postsUrl = path.join (__ dirname, 'db / posts.json'); const commentsUrl = path.join (__ dirname, 'db / comments.json'); // stuur de gegevens terug vanuit onze bestandsfunctie loadCollection (url) try const response = fs.readFileSync (url, 'utf8'); keer JSON.parse terug (antwoord); catch (fout) console.log (fout); // return een object op id-functie getRecord (verzameling, id) return collection.find (functie (element) return element.id == id;); // retourneer een reeks opmerkingen voor een postfunctie getCommentsByPost (comments, postId) return comments.filter (function (comment) return comment.postId == postId;); // initialisatiecode const posts = loadCollection (postsUrl); const post = getRecord (berichten, "001"); const comments = loadCollection (commentsUrl); const postComments = getCommentsByPost (opmerkingen, post.id); console.log (post); console.log (postComments);
["id": "001", "title": "Greeting", "text": "Hello World", "author": "Jane Doe", "id": "002", "title": "JavaScript 101", "text": "The fundamentals of programming.", "Author": "Alberta Williams", "id": "003", "title": "Async Programming", "text": " Callbacks, Promises en Async / Await. "," Author ":" Alberta Williams "]
["id": "phx732", "postId": "003", "text": "Ik krijg deze callback-dingen niet." , "id": "avj9438", "postId": "003", "text": "Dit is echt nuttige informatie." , "id": "gnk368", "postId": "001", "text": "Dit is een testopmerking." ]
De readFileSync
methode opent het bestand synchroon. Daarom kunnen we onze initialisatiecode ook synchroon schrijven. Maar dit is niet de beste manier om het bestand te openen, omdat dit een potentieel blokkerende taak is. Het openen van het bestand moet asynchroon worden gedaan, zodat de uitvoering kan worden voortgezet.
Knooppunt heeft een readFile
methode die we kunnen gebruiken om het bestand asynchroon te openen. Dit is de syntaxis:
fs.readFile (url, 'utf8', functie (fout, gegevens) ...);
We komen misschien in de verleiding om onze gegevens terug te sturen naar deze callback-functie, maar deze zal niet beschikbaar zijn voor ons om te gebruiken in onze loadCollection
functie. Onze initialisatiecode moet ook worden gewijzigd omdat we niet de juiste waarden hebben om aan onze variabelen toe te wijzen.
Om het probleem te illustreren, laten we een eenvoudiger voorbeeld bekijken. Wat denk je dat de volgende code zal afdrukken?
function task1 () setTimeout (function () console.log ('first');, 0); function task2 () console.log ('second'); function task3 () console.log ('third'); taak 1(); Task2 (); task3 ();
In dit voorbeeld wordt 'tweede', 'derde' en vervolgens 'eerste' afgedrukt. Het maakt niet uit dat het setTimeout
functie heeft een 0-vertraging. Het is een asynchrone taak in JavaScript, dus het wordt altijd uitgesteld om later uit te voeren. De firstTask
functie kan elke asynchrone taak vertegenwoordigen, zoals het openen van een bestand of het ondervragen van onze database.
Een oplossing om onze taken uit te voeren in de door ons gewenste volgorde is om callback-functies te gebruiken.
Als u terugbelfuncties wilt gebruiken, geeft u een functie door als parameter voor een andere functie en roept u de functie aan wanneer uw taak is voltooid. Als u een inleiding nodig hebt over het gebruik van functies van een hogere orde, heeft reactivex een interactieve zelfstudie die u kunt uitproberen.
Callbacks laten ons taken afdwingen om sequentieel uit te voeren. Ze helpen ons ook als we taken hebben die afhankelijk zijn van de resultaten van een vorige taak. Met behulp van callbacks kunnen we ons laatste voorbeeld repareren, zodat het 'eerst', 'tweede' en vervolgens 'derde' wordt afgedrukt.
function first (cb) setTimeout (function () return cb ('first');, 0); function second (cb) return cb ('second'); functie derde (cb) return cb ('derde'); first (function (result1) console.log (result1); second (function (result2) console.log (result2); third (function (result3) console.log (result3););); );
In plaats van de tekenreeks binnen elke functie af te drukken, retourneren we de waarde in een callback. Wanneer onze code wordt uitgevoerd, drukken we de waarde af die is doorgegeven aan onze callback. Dit is wat onze readFile
functie gebruikt.
Terugkerend naar onze bestanden, kunnen we onze veranderen loadCollection
zodat het callbacks gebruikt om het bestand asynchroon te lezen.
function loadCollection (url, callback) fs.readFile (url, 'utf8', functie (error, data) if (error) console.log (error); else return callback (JSON.parse (data)) ;);
En dit is hoe onze initialisatiecode eruit zal zien als we callbacks gebruiken:
loadCollection (postsUrl, function (posts) loadCollection (commentsUrl, function (comments) getRecord (posts, "001", function (post) const postComments = getCommentsByPost (comments, post.id); console.log (post); console.log (postComments););););
Een ding om op te merken in onze loadCollection
functie is dat in plaats van een proberen te vangen
verklaring om fouten te verwerken, gebruiken we een if / else
uitspraak. Het catch-blok kan geen fouten terughalen die zijn geretourneerd door de readFile
Bel terug.
Het is een goede gewoonte om foutbehandelaars in onze code te hebben voor fouten die het gevolg zijn van invloeden van buitenaf, in tegenstelling tot programmeerfouten. Dit omvat toegang tot bestanden, verbinding met een database of het maken van een HTTP-aanvraag.
In het herziene codevoorbeeld heb ik geen foutafhandeling opgenomen. Als er een fout zou optreden bij een van de stappen, zou het programma niet doorgaan. Het zou leuk zijn om zinvolle instructies te geven.
Een voorbeeld van wanneer foutafhandeling belangrijk is, is of we een taak hebben om in te loggen bij een gebruiker. Dit houdt in dat we een gebruikersnaam en wachtwoord uit een formulier halen, onze database bevragen om te zien of het een geldige combinatie is en de gebruiker vervolgens naar hun dashboard leiden als we succesvol zijn. Als de gebruikersnaam en het wachtwoord ongeldig zouden zijn, zou onze app stoppen als we hem niet vertellen wat hij moet doen.
Een betere gebruikerservaring zou zijn om een foutmelding naar de gebruiker terug te sturen en hen in staat te stellen opnieuw in te loggen. In ons bestandsvoorbeeld konden we een foutobject doorgeven in de callback-functie. Dit is het geval met de readFile
functie. Vervolgens kunnen we een code toevoegen als we de code uitvoeren if / else
verklaring om het succesvolle resultaat en het afgewezen resultaat af te handelen.
Gebruik de callback-methode om een programma te schrijven dat een bestand met gebruikers opent, een gebruiker selecteert en vervolgens een bestand met berichten opent en de info van de gebruiker en al zijn berichten afdrukt.
Asynchrone programmering is een methode die in onze code wordt gebruikt om gebeurtenissen uit te stellen voor latere uitvoering. Wanneer u te maken hebt met een asynchrone taak, zijn callbacks één oplossing voor het timen van onze taken, zodat ze sequentieel worden uitgevoerd.
Als we meerdere taken hebben die afhankelijk zijn van het resultaat van eerdere taken, is een oplossing om meerdere geneste callbacks te gebruiken. Dit kan echter leiden tot een probleem dat bekend staat als "callback hell". Beloften lossen het probleem op met callback hell en async-functies laten ons onze code op een synchrone manier schrijven. In deel 2 van deze tutorial zullen we leren wat ze zijn en hoe ze in onze code kunnen worden gebruikt.