Nodejs expresa y promete no hacer lo que espero

Intento crear una API de inicio de sesión utilizando NodeJS, pero mi código no está haciendo lo que esperaba. Soy muy nuevo en js, promesas y todo, así que simplifique cualquier respuesta si es posible.

Por lo que puedo ver en el resultado de mi código, la primera parte prometedora no espera hasta que la función findUsers(...) haya finalizado.

Tengo un archivo de rutas en el que quiero ejecutar algunas funciones de forma secuencial:

  1. Encuentre si el usuario existe en la base de datos
  2. if (1 es verdadero) Hash y salt la contraseña ingresada
  3. … etc

El archivo de rutas ahora contiene:

 var loginM = require('../models/login'); var loginC = require('../controllers/login'); var Promise = require('promise'); module.exports = function(app) { app.post('/login/', function(req, res, next) { var promise = new Promise(function (resolve, reject) { var rows = loginM.findUser(req.body, res); if (rows.length > 0) { console.log("Success"); resolve(rows); } else { console.log("Failed"); reject(reason); } }); promise.then(function(data) { return new Promise(function (resolve, reject) { loginC.doSomething(data); if (success) { console.log("Success 2"); resolve(data); } else { console.log("Failed 2"); reject(reason); } }); }, function (reason) { console.log("error handler second"); }); }); } 

Y la función findUser contiene pooling y una consulta y está en un archivo de modelos:

 var connection = require('../dbConnection'); var loginC = require('../controllers/login'); function Login() { var me = this; var pool = connection.getPool(); me.findUser = function(params, res) { var username = params.username; 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; //}); }); } } module.exports = new Login(); 

El resultado que obtengo es:

 Server listening on port 3000 Something is happening error handler second Connection 

Entonces, lo que quiero saber sobre este código es doble:

  1. ¿Por qué la primera promesa no está esperando que findUser regrese antes de continuar con el if / else y qué debo cambiar para que esto suceda?
  2. ¿Por qué el error handler second está error handler second pero no Failed ?

Siento que hay algo que estoy totalmente mal entendido acerca de las promesas. Estoy agradecido por cualquier respuesta. Gracias.

Problemas con el código

Ok, hay muchos problemas aquí, primero lo primero.

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

Esto no funcionará porque está devolviendo datos a la persona que llama, que es la consulta de la base de datos que llama a su callback con err y rows y no le importa el valor de devolución de su callback.

Lo que debe hacer es llamar a alguna otra función o método cuando tenga las filas o cuando no lo haga.

Estas llamando:

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

y espera obtener las filas allí, pero no lo hará. Lo que obtendrá undefined está undefined y lo obtendrá más rápido de lo que la consulta de la base de datos incluso se inicia. Funciona así:

 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 } 

El método pool.getConnection regresa inmediatamente después de pasar una función, incluso antes de que la conexión a la base de datos se realice. Luego, después de un tiempo, la función que pasaste a ese método puede ser llamada, pero pasará mucho tiempo después de que hayas regresado undefined al código que quería un valor en:

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

En lugar de devolver los valores de las devoluciones de llamada, debe llamar a algunas otras funciones o métodos (como algunas devoluciones de llamada que necesita llamar o un método para resolver una promesa).

Devolver un valor es un concepto sincrónico y no funcionará para el código asincrónico.


Cómo deberían usarse las promesas

Ahora, si su función devolvió una promesa :

 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'); } }); }); }); } 

entonces podrás usarlo en alguna otra parte de tu código de la siguiente manera:

 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); }); }); // ... 

Explicación

En resumen, si está ejecutando una operación asincrónica, como una consulta de base de datos, entonces no puede tener el valor inmediatamente así:

 var value = query(); 

porque el servidor necesitaría bloquear la espera de la base de datos antes de que pudiera ejecutar la asignación, y esto es lo que sucede en todos los lenguajes con E / S de locking síncronas (por eso es necesario que tengas hilos en esos idiomas para que otras cosas puedan ser hecho mientras ese hilo está bloqueado).

En Node, puede utilizar una función de callback que pase a la función asíncrona para recibir una llamada cuando tenga datos:

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

O puede obtener una promesa:

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

Pero en ambos casos, se ejecutará otherCode() inmediatamente después de registrar sus manejadores de callback o de promesa, antes de que la consulta tenga datos, es decir, no se debe realizar ningún locking.

Resumen

La idea es que en un entorno asíncrono, no bloqueante y de un único subproceso como Node.JS nunca se hace más de una cosa a la vez, pero se puede esperar un montón de cosas. Pero no solo esperas algo y no haces nada mientras esperas, planificas otras cosas, esperas más cosas y finalmente te devuelven la llamada cuando esté lista.

En realidad, escribí una historia corta en Medium para ilustrar ese concepto: I / O no negrita en el planeta Asynchronia256 / 16 – Una historia corta basada en hechos inciertos .