Mysql
 sql >> Base de données >  >> RDS >> Mysql

Nodejs exprime et promet de ne pas faire ce que j'attends

Problèmes avec le code

Ok, il y a beaucoup de problèmes ici, alors commençons par commencer.

        connection.query('...', function (err, rows) {
            connection.release();
            if (!err) {
                return rows;
            } else {
                return false;
            }
        });

Cela ne fonctionnera pas car vous renvoyez des données à l'appelant, qui est la requête de base de données qui appelle votre rappel avec err et rows et ne se soucie pas de la valeur de retour de votre rappel.

Ce que vous devez faire est d'appeler une autre fonction ou méthode lorsque vous avez les lignes ou lorsque vous n'en avez pas.

Vous appelez :

var rows = loginM.findUser(req.body, res);

et vous vous attendez à obtenir les lignes là-bas, mais vous ne le ferez pas. Ce que vous obtiendrez est undefined et vous l'obtiendrez plus rapidement que la requête de la base de données n'est même lancée. Cela fonctionne comme ceci :

me.findUser = function(params, res) {
    // (1) you save the username in a variable
    var username = params.username;

    // (2) you pass a function to getConnection method
    pool.getConnection(function (err, connection) {
        console.log("Connection ");

        if (err) {
            console.log("ERROR 1 ");
            res.send({"code": 100, "status": "Error in connection database"});
            return;
        }

        connection.query('select Id, Name, Password from Users ' +
            'where Users.Name = ?', [username], function (err, rows) {
            connection.release();
            if (!err) {
                return rows;
            } else {
                return false;
            }
        });

        //connection.on('error', function (err) {
        //    res.send({"code": 100, "status": "Error in connection database"});
        //    return;
        //});
    });

    // (3) you end a function and implicitly return undefined
}

Le pool.getConnection La méthode revient immédiatement après avoir passé une fonction, avant même que la connexion à la base de données ne soit établie. Ensuite, après un certain temps, cette fonction que vous avez transmise à cette méthode peut être appelée, mais cela prendra longtemps après que vous ayez déjà renvoyé undefined au code qui voulait une valeur dans :

var rows = loginM.findUser(req.body, res);

Au lieu de renvoyer des valeurs à partir de rappels, vous devez appeler d'autres fonctions ou méthodes (comme certains rappels que vous devez appeler ou une méthode pour résoudre une promesse).

Le renvoi d'une valeur est un concept synchrone et ne fonctionnera pas pour le code asynchrone.

Comment les promesses doivent être utilisées

Maintenant, si votre fonction a renvoyé une promesse :

me.findUser = function(params, res) {
    var username = params.username;

    return new Promise(function (res, rej) {

      pool.getConnection(function (err, connection) {
        console.log("Connection ");

        if (err) {
          rej('db error');
        } else {
          connection.query('...', [username], function (err, rows) {
            connection.release();
            if (!err) {
                res(rows);
            } else {
                rej('other error');
            }
        });
      });
    });
}

vous pourrez alors l'utiliser dans une autre partie de votre code d'une manière comme celle-ci :

app.post('/login/', function(req, res, next) {

    var promise = new Promise(function (resolve, reject) {

        // rows is a promise now:
        var rows = loginM.findUser(req.body, res);

        rows.then(function (rowsValue) {
            console.log("Success");
            resolve(rowsValue);
        }).catch(function (err) {
            console.log("Failed");
            reject(err);
        });
    });
    // ...

Explication

En résumé, si vous exécutez une opération asynchrone - comme une requête de base de données - alors vous ne pouvez pas avoir la valeur immédiatement comme ceci :

var value = query();

car le serveur aurait besoin de bloquer l'attente de la base de données avant de pouvoir exécuter l'affectation - et c'est ce qui se passe dans chaque langage avec des E/S bloquantes synchrones (c'est pourquoi vous devez avoir des threads dans ces langages pour que d'autres choses puissent être fait alors que ce fil est bloqué).

Dans Node, vous pouvez soit utiliser une fonction de rappel que vous transmettez à la fonction asynchrone pour être appelée lorsqu'elle contient des données :

query(function (error, data) {
  if (error) {
    // we have error
  } else {
    // we have data
  }
});
otherCode();

Ou vous pouvez obtenir une promesse :

var promise = query();
promise.then(function (data) {
  // we have data
}).catch(function (error) {
  // we have error
});
otherCode();

Mais dans les deux cas otherCode() sera exécuté immédiatement après l'enregistrement de vos gestionnaires de rappel ou de promesse, avant que la requête ne contienne des données - c'est-à-dire qu'aucun blocage ne doit être fait.

Résumé

L'idée est que dans un environnement asynchrone, non bloquant et à thread unique comme Node.JS, vous ne faites jamais plus d'une chose à la fois - mais vous pouvez attendre beaucoup de choses. Mais vous n'attendez pas simplement quelque chose et ne faites rien pendant que vous attendez, vous planifiez d'autres choses, attendez plus de choses, et finalement vous êtes rappelé quand c'est prêt.

En fait, j'ai écrit une courte histoire sur Medium pour illustrer ce concept :E/S non blacking sur la planète Asynchronia256/16 - Une courte histoire vaguement basée sur des faits incertains .