Fonctions de rappel Node.js

Node.js est un environnement d'exécution JavaScript basé sur le moteur Chrome V8. Il permet à JavaScript de s'exécuter côté serveur indépendamment du navigateur.

L'une des fonctionnalités principales de Node.js est son modèle d'E/S (entrée/sortie) non bloquant, qui rend Node.js très adapté à la gestion d'applications réseau à forte concurrence.

Les fonctions de rappel sont l'expression directe de la programmation asynchrone de Node.js.

Dans Node.js, une fonction de rappel est un modèle de programmation asynchrone utilisé pour gérer les opérations d'E/S telles que la lecture et l'écriture de fichiers, les interactions avec les bases de données et les requêtes réseau. Ces opérations prennent généralement beaucoup de temps ; si elles étaient traitées de manière synchrone, elles bloqueraient l'exécution de tout le programme jusqu'à ce que l'opération soit terminée.

Avec les fonctions de rappel, Node.js peut continuer à exécuter d'autre code pendant qu'une opération d'E/S est en cours, puis exécuter la fonction de rappel une fois l'opération d'E/S terminée.

Node.js est mono-thread, mais il réalise des opérations asynchrones grâce à un mécanisme événementiel et de rappel.

Cas d'utilisation

  • Opérations d'E/S telles que la lecture et l'écriture de fichiers.
  • Gestion des requêtes réseau.
  • Requêtes de base de données.

Par exemple, nous pouvons lire un fichier et exécuter d'autres commandes en même temps. Lorsque la lecture du fichier est terminée, nous renvoyons le contenu du fichier comme paramètres de la fonction de rappel. De cette façon, il n'y a pas de blocage ni d'attente pour les opérations d'E/S de fichier lors de l'exécution du code. Cela améliore considérablement les performances de Node.js et lui permet de gérer un grand nombre de requêtes simultanées.

Les fonctions de rappel apparaissent généralement comme le dernier paramètre d'une fonction :

function foo1(name, age, callback) { }
function foo2(value, callback1, callback2) { }

Exemple

Exemple de code bloquant

Créez un fichier input.txt avec le contenu suivant :

Example官网地址:www.example.com

Créez un fichier main.js avec le code suivant :

Exemple

var fs = require("fs");

var data = fs.readFileSync('input.txt');

console.log(data.toString());
console.log("L'exécution du programme est terminée !");

Le résultat d'exécution du code ci-dessus est le suivant :

$ node main.js
Example官网地址:www.example.com

程序执行结束!

Exemple de code non bloquant

Créez un fichier input.txt avec le contenu suivant :

Example官网地址:www.example.com

Créez un fichier main.js avec le code suivant :

Exemple

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
    if (err) return console.error(err);
    console.log(data.toString());
});

console.log("L'exécution du programme est terminée !");

Explication des paramètres:

  • errIl s'agit de l'objet d'erreur, et il aura une valeur si la lecture échoue.
  • dataIl s'agit du contenu du fichier qui a été lu.

Le résultat d'exécution du code ci-dessus est le suivant :

$ node main.js
程序执行结束!
Example官网地址:www.example.com

À partir des deux exemples ci-dessus, nous pouvons comprendre la différence entre les appels bloquants et non bloquants :

  • Le premier exemple n'exécute le programme qu'une fois la lecture du fichier terminée.

  • Dans le deuxième exemple, nous n'avons pas besoin d'attendre que la lecture du fichier soit terminée, nous pouvons donc exécuter le code suivant pendant la lecture du fichier, ce qui améliore grandement les performances du programme.

Par conséquent, le mode bloquant s'exécute séquentiellement, tandis que le mode non bloquant ne nécessite pas d'ordre séquentiel. Donc, si vous devez traiter les paramètres de la fonction de rappel, vous devez l'écrire à l'intérieur de la fonction de rappel.


Enfer des rappels

Lorsque plusieurs opérations asynchrones doivent être exécutées séquentiellement, les fonctions de rappel conduisent à du code imbriqué, ce qui rend le code difficile à lire et à maintenir.

Exemple

fs.readFile('file1.txt', 'utf8', (err, data1) => {
    if (err) {
        console.error('Erreur de lecture de file1 :', err);
        return;
    }

    fs.readFile('file2.txt', 'utf8', (err, data2) => {
        if (err) {
            console.error('Erreur de lecture de file2 :', err);
            return;
        }

        fs.readFile('file3.txt', 'utf8', (err, data3) => {
            if (err) {
                console.error('Erreur de lecture de file3 :', err);
                return;
            }

            console.log('Données de tous les fichiers :', data1, data2, data3);
        });
    });
});

Pour améliorer la lisibilité et la maintenabilité du code, vous pouvez utiliser les méthodes suivantes :

  • Promesses: Les Promesses sont un nouveau modèle de programmation asynchrone qui vous permet de gérer les opérations asynchrones de manière enchaînée, en évitant l'enfer des callbacks.
  • async/await: Basé sur les Promesses,async/awaitIl fournit un style de programmation asynchrone plus proche du code synchrone, ce qui rend le code asynchrone plus facile à comprendre et à maintenir.
  • Programmation événementielle: Node.js prend également en charge la programmation événementielle, en gérant les opérations asynchrones en écoutant et en émettant des événements.

1. Utiliser async/await

async/await est un sucre syntaxique introduit dans ES2017 qui vous permet de gérer les opérations asynchrones plus facilement et d'éviter l'enfer des callbacks.

Exemple

const fs = require('fs').promises;

async function readFiles() {
    try {
        const data1 = await fs.readFile('file1.txt', 'utf8');
        const data2 = await fs.readFile('file2.txt', 'utf8');
        const data3 = await fs.readFile('file3.txt', 'utf8');

        console.log('Données de tous les fichiers :', data1, data2, data3);
    } catch (err) {
        console.error('Erreur de lecture des fichiers:', err);
    }
}

readFiles();

2. Utiliser les promesses

Les promesses sont une autre façon de gérer les opérations asynchrones. Vous pouvez enchaîner les appels à la méthode then pour éviter les callbacks imbriqués.

Exemple

const fs = require('fs').promises;

fs.readFile('file1.txt', 'utf8')
    .then(data1 => {
        console.log('Données du fichier1 :', data1);
        return fs.readFile('file2.txt', 'utf8');
    })
    .then(data2 => {
        console.log('Données du fichier2 :', data2);
        return fs.readFile('file3.txt', 'utf8');
    })
    .then(data3 => {
        console.log('Données du fichier3 :', data3);
    })
    .catch(err => {
        console.error('Erreur de lecture des fichiers:', err);
    });

Les fonctions de rappel sont un moyen de base de gérer les opérations asynchrones dans Node.js, mais à mesure que la complexité des applications augmente, le problème de l'enfer des rappels devient de plus en plus apparent. En utilisant async/await ou des Promesses, la lisibilité et la maintenabilité du code peuvent être considérablement améliorées.

Autres extensions