我试图使用NodeJS构建一个登录API,但是我的代码并没有按照我的预期工作。我对js,promises都很陌生,所以如果可能的话,请简化任何答案。
从我的代码输出中可以看到,第一个promise部分不会等到函数findUsers(...)完成。
我有一个routes文件,希望在其中按顺序运行几个函数:
查找数据库中是否存在用户
如果(1为真)散列并对输入的密码加盐
... 等
路由文件现在包含:

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

所以我想知道的是两个方面:
为什么在处理if/else之前,第一个承诺没有等待findUser返回,我需要做什么更改才能发生这种情况?
为什么输出而不输出?
我觉得我对承诺完全误解了。
我很感激你的回答。谢谢。

最佳答案

代码的问题
好吧,这里有很多问题,所以首先要做的是。

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

不需要从回调返回值,而是需要从回调中调用其他一些函数或方法(例如需要调用的某些回调,或用于解析承诺的方法)。
返回值是一个同步概念,对异步代码不起作用。
如何使用承诺
现在,如果你的函数返回一个承诺:
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();

但在这两种情况下,在注册回调或promise处理程序之后,在查询拥有任何数据之前,otherCode()将立即运行,这是不必进行阻塞的。
摘要
整个想法是,在Node.JS这样的异步、非阻塞、单线程环境中,一次只做一件事,但可以等待很多事情。但你不只是等待,什么也不做,你在等待,你安排其他的事情,等待更多的事情,最终你会打电话回来时,它已经准备好了。
实际上,我写了一篇关于Medium的短篇文章来说明这个概念:Nonblacking I/O on the planet Asynchronia256/16 - A short story loosely based on uncertain facts

09-19 06:13