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

2021-11-20 00:00:00 node.js express javascript mysql promise

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

I am trying to build a login API using NodeJS, but my code is not doing what I expect it to. I am very new to js, promises and all so please simplify any answer if possible.

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

From what I can see in the output of my code, the first promise part does not wait until the function findUsers(...) is finished.

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

I have a routes file where I want to run a few functions sequentially:

  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 函数包含池化和查询,并位于模型文件中:

And the findUser function contains pooling and a query and is in a models file:

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

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

So what I want to know about this code is twofold:

  1. 为什么第一个 promise 在继续执行 if/else 之前不等待 findUser 返回,我需要更改什么才能发生这种情况?
  2. 为什么输出error handler second而不输出Failed?
  1. Why is the first promise not waiting for findUser to return before proceeding with the if/else and what do I need to change for this to happen?
  2. Why is error handler second outputed but not Failed?

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

I feel like there is something I am totally misunderstanding about promises. I am grateful for any answer. Thanks.

推荐答案

代码问题

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

Issues with the code

Ok, there are a lot of issues here so first things first.

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

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

This will not work because you are returning data to the caller, which is the database query that calls your callback with err and rows and doesn't care about the return value of your callback.

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

What you need to do is to call some other function or method when you have the rows or when you don't.

您正在致电:

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

并且您希望将行放在那里,但您不会.你会得到undefined,你会比数据库查询更快地得到它.它是这样工作的:

and you expect to get the rows there, but you won't. What you'll get is undefined and you'll get it quicker than the database query is even started. It works like this:

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 返回到需要值的代码之后很长时间:

The pool.getConnection method returns immediately after you pass a function, before the connection to the database is even made. Then, after some time, that function that you passed to that method may get called, but it will be long after you already returned undefined to the code that wanted a value in:

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

您需要调用其他一些函数或方法,而不是从回调中返回值(例如您需要调用的一些回调,或解析承诺的方法).

Instead of returning values from callbacks you need to call some other functions or methods from them (like some callbacks that you need to call, or a method to resolve a promise).

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

Returning a value is a synchronous concept and will not work for asynchronous code.

现在,如果您的函数返回一个promise:

Now, if your function returned a 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');
            }
        });
      });
    });
}

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

then you'll be able to use it in some other part of your code in a way like this:

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

说明

总而言之,如果你正在运行一个异步操作——比如数据库查询——那么你不能像这样立即获得值:

Explanation

In summary, if you are running an asynchronous operation - like a database query - then you can't have the value immediately like this:

var value = query();

因为服务器需要在执行分配之前阻塞等待数据库 - 这就是每种具有同步阻塞 I/O 的语言都会发生的情况(这就是为什么您需要在这些语言中使用线程,以便其他在那个线程被阻塞时可以做一些事情).

because the server would need to block waiting for the database before it could execute the assignment - and this is what happens in every language with synchronous, blocking I/O (that's why you need to have threads in those languages so that other things can be done while that thread is blocked).

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

In Node you can either use a callback function that you pass to the asynchronous function to get called when it has data:

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

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

Or you can get a promise:

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

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

But in both cases otherCode() will be run immediately after registering your callback or promise handlers, before the query has any data - that is no blocking has to be done.

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

The whole idea is that in an asynchronous, non-blocking, single-threaded environment like Node.JS you never do more than one thing at a time - but you can wait for a lot of things. But you don't just wait for something and do nothing while you're waiting, you schedule other things, wait for more things, and eventually you get called back when it's ready.

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

Actually I wrote a short story on Medium to illustrate that concept: Nonblacking I/O on the planet Asynchronia256/16 - A short story loosely based on uncertain facts.

相关文章