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

Posted

技术标签:

【中文标题】Nodejs 表达并承诺不会做我期望的事情【英文标题】:Nodejs express and promises not doing what I expect 【发布时间】:2017-02-03 03:16:48 【问题描述】:

我正在尝试使用 NodeJS 构建登录 API,但我的代码没有达到我的预期。我对 js、promise 和所有东西都很陌生,所以请尽可能简化任何答案。

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

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

    查找用户是否存在于数据库中 if(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

所以我想知道这段代码有两个方面:

    为什么第一个 Promise 没有等待 findUser 返回才继续执行 if/else,我需要进行哪些更改才能发生这种情况? 为什么输出的是error handler second而不是Failed

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

【问题讨论】:

【参考方案1】:

代码问题

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

        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 上写了一篇短篇小说来说明这个概念:Nonblacking I/O on the planet Asynchronia256/16 - A short story loosely based on uncertain facts。

【讨论】:

一个很好的答案,感谢您指出问题。我尝试应用您的解决方案,并且效果很好!现在我只需要再读几遍你的答案就可以完全理解所有内容了。

以上是关于Nodejs 表达并承诺不会做我期望的事情的主要内容,如果未能解决你的问题,请参考以下文章

Rxjs 可观察到承诺永远不会在 nodejs 中解决

Redux-Promise 不会阻止被拒绝的 Promise

高级软件工程--第八次作业

异常被承诺链吞噬

JavaScript NodeJS 如何将流/承诺与异步函数一起使用?

我担心由于使用对象的方式,这段代码没有做我想做的事情