Nodejs表达并承诺不做我期望的事情

时间:2016-09-25 20:42:56

标签: javascript mysql node.js express promise

我正在尝试使用NodeJS构建一个登录API,但我的代码没有按照我的预期进行。我对js,promises等都很陌生,如果可能的话,请简化任何答案。

从我在代码输出中看到的情况来看,第一个promise部分不会等到函数findUsers(...)完成。

我有一个路径文件,我想按顺序运行一些函数:

  1. 查找用户是否存在于数据库中
  2. if(1为true)哈希并加盐输入的密码
  3. ...... etc
  4. 路线文件现在包含:

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

    findUser函数包含池和查询,位于模型文件中:

    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();
    

    我得到的输出是:

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

    所以我想了解这段代码有两个方面:

    1. 为什么第一个承诺在继续使用if / else之前没有等待findUser返回,为此我需要更改什么?
    2. 为什么error handler second已经过时而不是Failed
    3. 我觉得有些事我完全误解了承诺。 我很感激任何答案。感谢。

1 个答案:

答案 0 :(得分:4)

代码问题

好的,这里有很多问题,所以首先要做的事情。

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

这不起作用,因为您将数据返回给调用者,调用者是使用errrows调用回调的数据库查询,并不关心您的返回值回调。

您需要做的是在有行或不做时调用其他功能或方法。

您正在致电:

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

你希望得到那些行,但你不会。您将获得的是undefined,并且您甚至可以比数据库查询更快地获得它。它的工作原理如下:

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
}

在传递函数之后,pool.getConnection方法立即返回,甚至在建立与数据库的连接之前。然后,经过一段时间后,您传递给该方法的函数可能会被调用,但是在您已经将undefined返回到需要值的代码之后很长一段时间:

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

不是从回调中返回值,而是需要从它们调用一些其他函数或方法(比如需要调用的一些回调或解析promise的方法)。

返回值是一个同步概念,不适用于异步代码。

应如何使用承诺

现在,如果您的函数返回承诺

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

然后您就可以通过以下方式在代码的其他部分中使用它:

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

说明

总之,如果您正在运行异步操作(如数据库查询),那么您不能立即获得该值:

var value = query();

因为服务器在执行赋值之前需要阻塞等待数据库 - 这就是每种语言都会发生同步,阻塞I / O的原因(这就是为什么你需要在那些中使用线程的原因)语言,以便在该线程被阻止时可以完成其他事情。)

在Node中,您可以使用传递给异步函数的回调函数,以便在有数据时调用它:

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

或者你可以得到一个承诺:

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

但是在这两种情况下,otherCode()将在注册回调或承诺处理程序之后立即运行,在查询有任何数据之前 - 不必阻塞。

摘要

整个想法是,在像Node.JS这样的异步,非阻塞,单线程环境中,你一次不会做多件事 - 但是你可以等待很多事情。但是,当你等待,你安排其他事情,等待更多事情,并最终在准备就绪时回电话时,你不会等待什么,什么都不做,最后你会在准备就绪时回电话。

实际上我在Medium上写了一篇简短的故事来说明这个概念:this post