当前位置: 首页 > 面试题库 >

Node.js表达并承诺不做我期望的事情

暴德运
2023-03-14
问题内容

我正在尝试使用NodeJS构建登录API,但是我的代码没有达到我的期望。我对js,promise和其他所有语言都是新手,因此请尽可能简化任何答案。

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

我有一个路由文件,我要在其中依次运行一些功能:

  1. 查找用户是否存在于数据库中
  2. if(1为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. 为什么第一个诺言findUser在继续进行if / else之前不等待返回,我需要做些什么改变才能做到这一点?
  2. 为什么error handler second输出但不输出Failed

我觉得我对诺言完全有些误解。感谢您的回答。谢谢。


问题答案:

代码问题

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

        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

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()将在注册您的回调或promise处理程序后立即运行,而不需要查询包含任何数据,这无需执行阻止操作。

摘要

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

实际上,我在Medium上写了一个简短的故事来说明这个概念:在Asynchronia256 / 16星球上避免I / O变黑-
基于不确定的事实,一个松散的简短故事



 类似资料:
  • 问题内容: 据我了解,有三种调用异步代码的方法: 活动,例如 回调,例如 承诺 我找到了节点承诺库,但是我不明白。 有人可以解释什么是诺言,为什么我要使用它? 另外,为什么将它从Node.js中删除? 问题答案: node.js中的承诺承诺会做一些工作,然后有单独的回调,将为成功和失败以及处理超时而执行。在node.js中考虑promise的另一种方式是,它们是可能仅发射两个事件的发射器:成功和错

  • 问题内容: 获取错误以及上面的代码..如果有人启发我,那将是非常好.. 问题答案: 将Xcode更新为7.3新的#selector语法仅在Xcode 7.3(或更高版本)中有效

  • 问题内容: 我正在使用promis模块从请求模块返回我的json数据,但是每次运行它时,它都会为我提供此信息。 我无法正常工作,有人知道这个问题吗?这是我的代码: 问题答案: 许诺是充当未来价值的占位符的对象。您的函数返回该Promise对象。通过将处理程序附加到promise,您可以在promise中获得未来的价值: 这是异步代码,因此,仅能通过处理程序来获得承诺的价值。 修改清单: 在返回的p

  • 问题内容: 我想做一些非常简单的事情,但是我一点都不懂… 我希望依次对funcs数组的每个元素进行“处理”,然后输出如下所示: 我无法做到这一点:(应该很简单,我什么都没发现:( 问题答案: 从您的示例来看,我认为您已经看到了Q自述文件中的Sequences部分,但未能理解它。 当每个函数的输出作为输入传递给下一个函数时,原始示例使用“瀑布”模型: 但是您只想按顺序执行我们所有的函数,因此您只需将

  • 问题内容: 我有以下状态: 然后我更新状态: 由于setState是假设要合并的,所以我希望它是: 但是它吃掉了id,状态为: 这是预期的行为吗?仅更新嵌套状态对象的一个​​属性的解决方案是什么? 问题答案: 我认为不做递归合并。 您可以使用当前状态的值构造一个新状态,然后调用该状态: 我在这里使用过函数function(来自underscore.js库),通过创建状态的浅表副本来防止对该状态的现

  • 问题内容: 这不是现实问题,我只是想了解如何创建承诺。 我需要了解如何对不返回任何内容的函数(例如setTimeout)作出承诺。 假设我有: 如何创建可以在准备就绪后返回的承诺? 我本来应该把它包起来带到某个地方: 但是我无法超越这个范围。 问题答案: 更新 在2017年,Promises内置在JavaScript中,并由ES2015规范添加(polyfill可用于IE8-IE11等过时的环境)