Node.js
Programmation asynchrone
Recherche…
Introduction
Node est un langage de programmation où tout peut être exécuté de manière asynchrone. Vous trouverez ci-dessous des exemples et des exemples typiques de travail asynchrone.
Syntaxe
- doQuelque chose ([args], function ([argsCB]) {/ * fait quelque chose quand c'est fait * /});
- doQuelque chose ([args], ([argsCB]) => {/ * fait quelque chose quand c'est fait * /});
Fonctions de rappel
Fonctions de rappel en JavaScript
Les fonctions de rappel sont communes en JavaScript. Les fonctions de rappel sont possibles en JavaScript car les fonctions sont des citoyens de premier ordre .
Rappels synchrones.
Les fonctions de rappel peuvent être synchrones ou asynchrones. Comme les fonctions de rappel asynchrones peuvent être plus complexes, voici un exemple simple de fonction de rappel synchrone.
// 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");
La sortie du code ci-dessus est:
> Before getSyncMessage call
> Hello World!
> After getSyncMessage call
Nous allons d'abord passer en revue comment le code ci-dessus est exécuté. C'est plus pour ceux qui ne comprennent pas déjà le concept des rappels si vous comprenez déjà que vous pouvez sauter ce paragraphe. Tout d'abord, le code est analysé, puis la première chose intéressante à faire est que la ligne 6 soit exécutée, ce qui Before getSyncMessage call
à la console. Ensuite, la ligne 8 est exécutée et appelle la fonction getSyncMessage
envoyant une fonction anonyme en tant qu'argument du paramètre nommé cb
dans la fonction getSyncMessage
. L'exécution est maintenant effectuée dans la fonction getSyncMessage
de la ligne 3 qui exécute la fonction cb
qui vient d'être transmise, cet appel envoie une chaîne d'argument "Hello World" pour le message
nommé param dans la fonction anonyme passée. L'exécution va ensuite à la ligne 9 qui connecte Hello World!
à la console. Ensuite, l'exécution passe par le processus de sortie de la pile d'appels ( voir aussi ) en frappant la ligne 10 puis la ligne 4 puis finalement en revenant à la ligne 11.
Quelques informations à connaître sur les rappels en général:
- La fonction que vous envoyez à une fonction en tant que rappel peut être appelée zéro fois, une fois ou plusieurs fois. Tout dépend de la mise en œuvre.
- La fonction de rappel peut être appelée de manière synchrone ou asynchrone et éventuellement de manière synchrone et asynchrone.
- Tout comme les fonctions normales, les noms que vous attribuez aux paramètres de votre fonction ne sont pas importants, mais l'ordre est. Ainsi, par exemple, à la ligne 8, le
message
paramètre pourrait avoir été nomméstatement
,msg
ou si vous êtes insensé, commejellybean
. Vous devez donc savoir quels paramètres sont envoyés dans votre callback pour que vous puissiez les obtenir dans le bon ordre avec des noms propres.
Rappels asynchrones.
Une chose à noter à propos de JavaScript est qu'il est synchrone par défaut, mais il existe des API dans l'environnement (navigateur, Node.js, etc.) qui pourraient le rendre asynchrone (il y a plus à ce sujet ici ).
Quelques éléments communs qui sont asynchrones dans les environnements JavaScript qui acceptent les rappels:
- Événements
- setTimeout
- mettreInterval
- l'API d'extraction
- Promesses
De plus, toute fonction qui utilise l'une des fonctions ci-dessus peut être entourée d'une fonction qui prend un rappel et le rappel serait alors un rappel asynchrone (bien que l'encapsulation d'une promesse avec une fonction prenant un rappel serait probablement considérée comme un anti-pattern comme il y a plus de moyens préférés pour gérer les promesses).
Donc, étant donné cette information, nous pouvons construire une fonction asynchrone similaire à celle ci-dessus synchrone.
// 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");
Qui imprime les éléments suivants sur la console:
> Before getSyncMessage call
> After getSyncMessage call
// pauses for 1000 ms with no output
> Hello World!
L'exécution de ligne passe à la ligne 6 des journaux "Avant l'appel getSyncMessage". Ensuite, l'exécution va à la ligne 8 appelant getAsyncMessage avec un rappel pour le paramètre cb
. La ligne 3 est alors exécutée et appelle setTimeout avec un rappel comme premier argument et le numéro 300 comme second argument. setTimeout
fait tout ce qu'il fait et retient ce rappel pour pouvoir l'appeler plus tard en 1000 millisecondes, mais après avoir configuré le délai d'attente et avant de suspendre les 1000 millisecondes, il renvoie l'exécution à la ligne 4 , puis la ligne 11, puis s'arrête pendant 1 seconde et setTimeout appelle alors sa fonction de rappel qui getAsyncMessages
exécution sur la ligne 3 où le rappel getAsyncMessages
est appelé avec la valeur "Hello World" pour son message
paramètre qui est ensuite consigné sur la ligne 9 .
Fonctions de rappel dans Node.js
NodeJS a des rappels asynchrones et fournit généralement deux paramètres à vos fonctions, parfois appelés de manière conventionnelle err
et data
. Un exemple avec la lecture d'un fichier texte.
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
}
});
Ceci est un exemple de rappel appelé une seule fois.
Il est recommandé de gérer l'erreur d'une manière ou d'une autre, même si vous ne faites que la connecter ou la lancer. Le reste n'est pas nécessaire si vous lancez ou renvoyez et que vous pouvez le supprimer pour diminuer l'indentation tant que vous arrêtez l'exécution de la fonction en cours dans le if en faisant quelque chose comme lancer ou retourner.
Bien qu'il soit fréquent de voir err
, les data
ne peut pas toujours être le cas que vos callbacks utiliseront ce modèle , il est préférable de regarder la documentation.
Un autre exemple de rappel provient de la bibliothèque express (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);
Cet exemple montre un rappel appelé plusieurs fois. Le rappel est fourni avec deux objets en tant que paramètres nommés ici comme req
et res
ces noms correspondent respectivement à la requête et à la réponse, et ils permettent de visualiser la requête et de définir la réponse qui sera envoyée à l'utilisateur.
Comme vous pouvez le voir, il existe différentes manières d’utiliser un rappel pour exécuter la synchronisation et le code asynchrone en JavaScript et les rappels sont très répandus dans JavaScript.
Exemple de code
Question: Quelle est la sortie du code ci-dessous et pourquoi?
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");
Sortie: Ceci est connu avec certitude: EBAD
. C
est inconnu quand il sera enregistré.
Explication: Le compilateur ne s'arrêtera pas sur les méthodes setTimeout
et getDataFromDatabase
. Donc, la première ligne qu'il enregistrera est E
Les fonctions de rappel (premier argument de setTimeout
) seront exécutées après le délai d'attente défini de manière asynchrone!
Plus de détails:
-
E
n'a pas desetTimeout
-
B
a un délai d'attente de 0 milliseconde -
A
a un délai d'attente de 1000 millisecondes -
D
doit demander une base de données, après il doitD
attendre 1000 millisecondes il vient aprèsA
. -
C
est inconnu car il est inconnu lorsque les données de la base de données sont demandées. Cela pourrait être avant ou aprèsA
Traitement d'erreur asynchrone
Essayer attraper
Les erreurs doivent toujours être traitées. Si vous utilisez une programmation synchrone, vous pouvez utiliser un try catch
. Mais cela ne fonctionne pas si vous travaillez asynchrone! Exemple:
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);
}
Les erreurs asynchrones ne seront traitées que dans la fonction de rappel!
Possibilités de travail
Gestionnaires d'événements
Les premières versions de Node.JS ont un gestionnaire d'événement.
process.on("UncaughtException", function(err, data) {
if (err) {
// error handling
}
});
Domaines
Dans un domaine, les erreurs sont libérées via les émetteurs d'événements. En utilisant ceci, il y a toutes les erreurs, les temporisateurs, les méthodes de rappel implicitement seulement enregistrées dans le domaine. Par une erreur, être un événement d'erreur envoyé et ne pas planter l'application.
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);
});
Enfer de rappel
Callback hell (également une pyramide d'effet doom ou boomerang) se produit lorsque vous imbriquez trop de fonctions de rappel dans une fonction de rappel. Voici un exemple pour lire un fichier (dans 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");
}
});
Comment éviter "Callback Hell"
Il est recommandé de ne pas imbriquer plus de 2 fonctions de rappel. Cela vous aidera à maintenir la lisibilité du code et sera beaucoup plus facile à maintenir à l'avenir. Si vous devez imbriquer plus de 2 rappels, essayez plutôt d'utiliser des événements distribués .
Il existe également une bibliothèque appelée async qui aide à gérer les callbacks et leur exécution disponible sur npm. Il améliore la lisibilité du code de rappel et vous permet de mieux contrôler le flux de votre code de rappel, en vous permettant notamment de les exécuter en parallèle ou en série.
Promesses autochtones
Les promesses sont un outil de programmation asynchrone. En JavaScript, les promesses sont connues pour leurs méthodes then
. Les promesses ont deux états principaux «en attente» et «réglé». Une fois qu'une promesse est «réglée», elle ne peut plus être «en attente». Cela signifie que les promesses sont généralement bonnes pour les événements qui ne se produisent qu'une seule fois. L'état "réglé" a deux états aussi "résolu" et "rejeté". Vous pouvez créer une nouvelle promesse en utilisant le new
mot-clé et en passant une fonction dans le constructeur new Promise(function (resolve, reject) {})
.
La fonction transmise au constructeur Promise reçoit toujours un premier et un deuxième paramètre, généralement nommés resolve
et reject
respectivement. La désignation de ces deux paramètres est la convention, mais ils mettront la promesse soit dans l'état «résolu», soit dans l'état «rejeté». Lorsque l’une d’elles est appelée, la promesse passe de «en attente» à «réglé». resolve
est appelée lorsque l'action désirée, qui est souvent asynchrone, a été effectuée et que le reject
est utilisé si l'action a été erronée.
Dans le délai ci-dessous, une fonction renvoie une promesse.
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...");
sortie de la console
waiting...
// << pauses for one second>>
It was resolved!
Lorsque la temporisation est appelée, la fonction transmise au constructeur Promise est exécutée sans délai. Ensuite, la méthode setTimeout est exécutée et son rappel est défini pour se déclencher dans les ms
millisecondes suivantes, dans ce cas, ms=1000
. Comme le rappel de setTimeout n'est pas déclenché, la fonction de délai d'attente renvoie le contrôle à la portée de l'appel. La chaîne d' then
les méthodes sont ensuite stockées à appeler plus tard , quand / si la promesse a résolu. S'il y avait catch
méthodes de catch
ici, elles seraient également stockées, mais seraient renvoyées quand / si la promesse est «rejetée».
Le script imprime alors "en attente ...". Une seconde plus tard, setTimeout appelle son rappel qui appelle la fonction resolve avec la chaîne "It a été résolue!". Cette chaîne est ensuite passé dans le then
de rappel de la méthode et est ensuite connecté à l'utilisateur.
Dans le même sens, vous pouvez encapsuler la fonction asynchrone setTimeout qui nécessite un rappel, vous pouvez envelopper toute action asynchrone singulière avec une promesse.
En savoir plus sur les promesses dans la documentation JavaScript promesses .