Nodejs 表达并Promise不会做我期望的事情

IT技术 javascript mysql node.js express promise
2021-02-04 13:11:19

我正在尝试使用 NodeJS 构建登录 API,但我的代码没有按照我的预期运行。我对 js、promise 和所有内容都很陌生,因此请尽可能简化任何答案。

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

我有一个路由文件,我想在其中按顺序运行一些函数:

  1. 查找用户是否存在于数据库中
  2. if(1 is true) 对输入的密码进行散列和加盐
  3. ... 等等

路由文件现在包含:

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. 为什么第一个PromisefindUser在继续执行 if/else 之前不等待返回,我需要改变什么才能发生这种情况?
  2. 为什么是error handler second输出而不是Failed

我觉得我完全误解了Promise。我很感激任何答案。谢谢。

1个回答

代码问题

好的,这里有很多问题,所以首先要解决问题。

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

因为你将数据返回给调用者,这是数据库查询与调用你的回调,这将无法正常工作err,并rows和不关心你的回调函数的返回值。

您需要做的是在有行或没有行时调用其他一些函数或方法。

您正在致电:

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的方法)。

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


应如何使用Promise

现在,如果您的函数返回了一个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();

或者你可以得到一个Promise:

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

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

概括

整个想法是,在像 Node.JS 这样的异步、非阻塞、单线程环境中,你一次做的事情永远不会超过一件事——但你可以等待很多事情。但是,您不会只是等待某事而在等待时什么也不做,您还可以安排其他事情,等待更多事情,最终当它准备好时会被召回。

实际上,我在 Medium 上写了一篇短篇小说来说明这个概念:Asynchronia256/16 星球上的非黑化 I/O - 一个基于不确定事实的短篇小说

一个很好的答案,感谢您指出问题。我尝试应用您的解决方案,效果很好!现在我只需要多读几遍您的答案即可完全理解所有内容。
2021-03-25 13:11:19