Node.js
Asynchroon programmeren
Zoeken…
Invoering
Node is een programmeertaal waarin alles asynchroon kan worden uitgevoerd. Hieronder vindt u enkele voorbeelden en de typische dingen van asynchroon werken.
Syntaxis
- doSomething ([args], function ([argsCB]) {/ * doe iets als u klaar bent * /});
- doSomething ([args], ([argsCB]) => {/ * doe iets als u klaar bent * /});
Terugbel functies
Callback-functies in JavaScript
Callback-functies zijn gebruikelijk in JavaScript. Terugbelfuncties zijn mogelijk in JavaScript omdat functies eersteklas burgers zijn .
Synchrone callbacks.
Terugbelfuncties kunnen synchroon of asynchroon zijn. Omdat asynchrone terugbelfuncties complexer kunnen zijn, is hier een eenvoudig voorbeeld van een synchrone terugbelfunctie.
// a function that uses a callback named `cb` as a parameter
function getSyncMessage(cb) {
cb("Hello World!");
}
console.log("Before getSyncMessage call");
// calling a function and sending in a callback function as an argument.
getSyncMessage(function(message) {
console.log(message);
});
console.log("After getSyncMessage call");
De output voor de bovenstaande code is:
> Before getSyncMessage call
> Hello World!
> After getSyncMessage call
Eerst zullen we doorlopen hoe de bovenstaande code wordt uitgevoerd. Dit is meer voor degenen die het concept van callbacks nog niet begrijpen, als u het al begrijpt, kunt u deze paragraaf overslaan. Eerst wordt de code ontleed en dan is het eerste interessante wat er gebeurt is dat regel 6 wordt uitgevoerd die uitgangen Before getSyncMessage call
de console Before getSyncMessage call
. Vervolgens wordt regel 8 uitgevoerd die de functie getSyncMessage
en een anonieme functie verzendt als argument voor de parameter met de naam cb
in de functie getSyncMessage
. De uitvoering vindt nu plaats in de functie getSyncMessage
op regel 3 die de functie cb
uitvoert die zojuist is doorgegeven. Deze aanroep verzendt een argumentstring "Hallo wereld" voor het param-benoemde message
in de anonieme doorgegeven functie. De uitvoering gaat dan naar regel 9 die Hello World!
registreert Hello World!
naar de console. Vervolgens gaat de uitvoering door het proces van het verlaten van de callstack ( zie ook ) door regel 10 te raken en vervolgens regel 4 en uiteindelijk terug naar regel 11.
Enkele informatie over callbacks in het algemeen:
- De functie die u als terugbelopdracht naar een functie verzendt, kan nul keer, één keer of meerdere keren worden genoemd. Het hangt allemaal af van de implementatie.
- De terugbelfunctie kan synchroon of asynchroon en mogelijk zowel synchroon als asynchroon worden genoemd.
- Net als normale functies zijn de namen die u parameters geeft voor uw functie niet belangrijk, maar de volgorde is. Dus bijvoorbeeld op lijn 8 de parameter
message
had kunnen worden genoemdstatement
,msg
, of als u dat onzinnig iets alsjellybean
. U moet dus weten welke parameters naar uw callback worden verzonden, zodat u ze in de juiste volgorde met de juiste namen kunt krijgen.
Asynchrone callbacks.
Een ding op te merken over Javascript is dat het synchroon standaard, maar er zijn API's die in het milieu (browser, Node.js, etc.) die het kon laten asynchrone (er is meer daarover hier ).
Enkele veel voorkomende dingen die asynchroon zijn in JavaScript-omgevingen die callbacks accepteren:
- Evenementen
- setTimeout
- setInterval
- de ophaal-API
- Promises
Elke functie die een van de bovenstaande functies gebruikt, kan ook worden ingepakt met een functie die wordt teruggebeld en de terugbelactie zou dan een asynchrone terugbelactie zijn (hoewel het inpakken van een belofte met een functie die teruggebeld wordt waarschijnlijk als een antipatroon wordt beschouwd als er zijn meer geprefereerde manieren om beloften af te handelen).
Dus gezien die informatie kunnen we een asynchrone functie bouwen vergelijkbaar met de bovenstaande synchrone functie.
// a function that uses a callback named `cb` as a parameter
function getAsyncMessage(cb) {
setTimeout(function () { cb("Hello World!") }, 1000);
}
console.log("Before getSyncMessage call");
// calling a function and sending in a callback function as an argument.
getAsyncMessage(function(message) {
console.log(message);
});
console.log("After getSyncMessage call");
Hiermee wordt het volgende naar de console afgedrukt:
> Before getSyncMessage call
> After getSyncMessage call
// pauses for 1000 ms with no output
> Hello World!
De uitvoering van de lijn gaat naar de logs van regel 6 "Voordat getSyncMessage-aanroep". Vervolgens gaat de uitvoering naar regel 8 die getAsyncMessage aanroept met een callback voor de param cb
. Vervolgens wordt regel 3 uitgevoerd die setTimeout aanroept met een callback als het eerste argument en het getal 300 als het tweede argument. setTimeout
doet wat het doet en houdt die callback vast zodat het het later in 1000 milliseconden kan oproepen, maar na het instellen van de time-out en voordat het de 1000 milliseconden pauzeert, geeft het de uitvoering terug naar waar het was gestopt, zodat het naar regel 4 gaat , dan lijn 11, en dan pauzeert gedurende 1 seconde en setTimeout roept vervolgens de callback functie die uitvoering terug naar lijn 3, waar neemt getAsyncMessages
callback wordt aangeroepen met de waarde "Hello World" voor zijn parameter message
die vervolgens naar de console is aangemeld op lijn 9 .
Callback-functies in Node.js
NodeJS heeft asynchrone callbacks en levert gewoonlijk twee parameters voor uw functies, die soms conventioneel err
en data
. Een voorbeeld van het lezen van een bestandstekst.
const fs = require("fs");
fs.readFile("./test.txt", "utf8", function(err, data) {
if(err) {
// handle the error
} else {
// process the file text given with data
}
});
Dit is een voorbeeld van een callback die eenmalig wordt genoemd.
Het is een goede gewoonte om op de een of andere manier met de fout om te gaan, ook al logt u hem gewoon in of gooit u hem. De rest is niet nodig als je gooit of terugkeert en kan worden verwijderd om de inspringing te verminderen zolang je de uitvoering van de huidige functie in het if stopt door iets te doen zoals gooien of terugkeren.
Hoewel het gebruikelijk om te zien kunnen zijn err
, data
kan niet altijd het geval dat uw callbacks dat patroon is het het beste om te kijken naar documentatie gebruiken.
Een ander voorbeeld van een callback komt uit de expressbibliotheek (express 4.x):
// this code snippet was on http://expressjs.com/en/4x/api.html
const express = require('express');
const app = express();
// this app.get method takes a url route to watch for and a callback
// to call whenever that route is requested by a user.
app.get('/', function(req, res){
res.send('hello world');
});
app.listen(3000);
Dit voorbeeld toont een callback die meerdere keren wordt aangeroepen. De terugbelactie is voorzien van twee objecten als params die hier worden aangeduid als req
en res
Deze namen komen overeen met respectievelijk verzoek en antwoord, en ze bieden manieren om het binnenkomende verzoek te bekijken en het antwoord in te stellen dat naar de gebruiker zal worden verzonden.
Zoals u kunt zien, zijn er verschillende manieren waarop een callback kan worden gebruikt om sync- en async-code in JavaScript uit te voeren en callbacks zijn overal in JavaScript alomtegenwoordig.
Code voorbeeld
Vraag: Wat is de output van onderstaande code en waarom?
setTimeout(function() {
console.log("A");
}, 1000);
setTimeout(function() {
console.log("B");
}, 0);
getDataFromDatabase(function(err, data) {
console.log("C");
setTimeout(function() {
console.log("D");
}, 1000);
});
console.log("E");
Output: dit is zeker bekend: EBAD
. C
is onbekend wanneer het zal worden vastgelegd.
Verklaring: De compiler stopt niet op de getDataFromDatabase
setTimeout
en getDataFromDatabase
. Dus de eerste regel die hij zal loggen is E
De callback-functies (eerste argument van setTimeout
) worden na de ingestelde time-out asynchroon uitgevoerd!
Meer details:
-
E
heeft geensetTimeout
-
B
heeft een ingestelde time-out van 0 milliseconden -
A
heeft een ingestelde time-out van 1000 milliseconden -
D
moet een database aanvragen, nadat dezeD
1000 milliseconden moet wachten, zodat deze naA
komt. -
C
is onbekend omdat het onbekend is wanneer de gegevens van de database worden opgevraagd. Het kan voor of naA
.
Async foutafhandeling
Proberen te vangen
Fouten moeten altijd worden behandeld. Als u synchroon programmeren gebruikt, kunt u een try catch
. Maar dit werkt niet als u asynchroon werkt! Voorbeeld:
try {
setTimeout(function() {
throw new Error("I'm an uncaught error and will stop the server!");
}, 100);
}
catch (ex) {
console.error("This error will not be work in an asynchronous situation: " + ex);
}
Async-fouten worden alleen binnen de callback-functie afgehandeld!
Werk mogelijkheden
Event handlers
De eerste versies van Node.JS hebben een gebeurtenishandler.
process.on("UncaughtException", function(err, data) {
if (err) {
// error handling
}
});
domeinen
Binnen een domein worden de fouten vrijgegeven via de event-emitters. Door dit te gebruiken worden alle fouten, timers, callback-methoden impliciet alleen geregistreerd binnen het domein. Door een fout, stuur een fout en stuur de applicatie niet.
var domain = require("domain");
var d1 = domain.create();
var d2 = domain.create();
d1.run(function() {
d2.add(setTimeout(function() {
throw new Error("error on the timer of domain 2");
}, 0));
});
d1.on("error", function(err) {
console.log("error at domain 1: " + err);
});
d2.on("error", function(err) {
console.log("error at domain 2: " + err);
});
Terugbellen hel
Callback hell (ook een piramide van doom of boemerangeffect) ontstaat wanneer u te veel callback-functies in een callback-functie nestelt. Hier is een voorbeeld om een bestand te lezen (in ES6).
const fs = require('fs');
let filename = `${__dirname}/myfile.txt`;
fs.exists(filename, exists => {
if (exists) {
fs.stat(filename, (err, stats) => {
if (err) {
throw err;
}
if (stats.isFile()) {
fs.readFile(filename, null, (err, data) => {
if (err) {
throw err;
}
console.log(data);
});
}
else {
throw new Error("This location contains not a file");
}
});
}
else {
throw new Error("404: file not found");
}
});
Hoe "Callback Hell" te voorkomen
Het wordt aanbevolen om niet meer dan 2 callback-functies te nesten. Dit zal u helpen om de leesbaarheid van de code te behouden en zal mij in de toekomst veel gemakkelijker te onderhouden zijn. Als u meer dan 2 callbacks wilt nesten, probeer dan in plaats daarvan gedistribueerde gebeurtenissen te gebruiken.
Er bestaat ook een bibliotheek genaamd async die helpt bij het beheren van callbacks en de uitvoering ervan beschikbaar op npm. Het verhoogt de leesbaarheid van callback-code en geeft u meer controle over uw callback-codestroom, inclusief de mogelijkheid om ze parallel of in serie uit te voeren.
Inheemse beloften
Beloften zijn een hulpmiddel voor async-programmering. In JavaScript staan beloften bekend om hun then
methoden. Beloften hebben twee hoofdstaten 'in behandeling' en 'geregeld'. Als een belofte eenmaal 'geregeld' is, kan deze niet meer teruggaan naar 'in behandeling'. Dit betekent dat beloften meestal goed zijn voor evenementen die maar één keer voorkomen. De 'gevestigde' staat heeft ook twee staten 'opgelost' en 'afgewezen'. U kunt een nieuwe belofte maken met behulp van het new
trefwoord en een functie doorgeven aan de new Promise(function (resolve, reject) {})
de constructor new Promise(function (resolve, reject) {})
.
De functie die wordt doorgegeven aan de Promise-constructor ontvangt altijd een eerste en tweede parameter die meestal respectievelijk resolve
en reject
genoemd. De naamgeving van deze twee parameters is een conventie, maar ze zullen de belofte in de 'opgeloste' staat of de 'afgewezen' staat brengen. Wanneer een van deze twee wordt genoemd, gaat de belofte van 'in behandeling' naar 'geregeld'. resolve
wordt aangeroepen wanneer de gewenste actie, die vaak asynchroon is, is uitgevoerd en reject
wordt gebruikt als de actie een fout heeft gemaakt.
In de onderstaande time-out staat een functie die een belofte retourneert.
function timeout (ms) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("It was resolved!");
}, ms)
});
}
timeout(1000).then(function (dataFromPromise) {
// logs "It was resolved!"
console.log(dataFromPromise);
})
console.log("waiting...");
console uitgang
waiting...
// << pauses for one second>>
It was resolved!
Wanneer de time-out wordt aangeroepen, wordt de functie die is doorgegeven aan de Promise-constructor onmiddellijk uitgevoerd. Vervolgens wordt de methode setTimeout uitgevoerd en wordt de callback in de volgende ms
milliseconden geactiveerd, in dit geval ms=1000
. Omdat de callback naar setTimeout nog niet is geactiveerd, geeft de time-outfunctie de besturing terug aan het aanroepbereik. De keten then
werkwijzen worden dan opgeslagen om later te worden opgeroepen wanneer / indien de belofte is opgelost. Als er catch
methoden hier zouden ze worden opgeslagen als goed, maar zou worden ontslagen wanneer / als de belofte 'uitval'.
Het script drukt vervolgens 'wachten ...' af. Een seconde later roept de setTimeout zijn callback aan die de oplossingsfunctie oproept met de string "Het was opgelost!". Die string wordt vervolgens doorgegeven aan de callback van de then
methode en vervolgens vastgelegd in de gebruiker.
Op dezelfde manier kunt u de asynchrone setTimeout-functie omwikkelen waarvoor een callback nodig is. U kunt elke asynchrone actie met een belofte omwikkelen.
Lees meer over beloften in de JavaScript-documentatie Beloften .