0

我刚刚开始尝试使用 node(使用 Express 构建一个带有 MySql 数据库的简单网站)。

我基本上已经使用 Express 提供的应用程序结构(这对于这个问题来说并不重要)。我有一个文件routes/index.js,它导出index每当对我的主页发出请求时都会被命中的函数。的内容index.js是:

var db = require('../db');                                                      

exports.index = function(req, res){                                             
    db.getConnection(function(err, connection) {                                
        connection.query('SELECT * FROM test_table', function (err, rows) {     
            var templateVariables = {                                           
                title: 'Index Page',                                            
                response: rows[0].text                                          
            };                                                                  
            res.render('index', templateVariables);                             
        });                                                                     
        connection.end();                                                                                     
    });                                                                                                       
};   

这显然是一个非常初步和轻量级的示例,但是在GET索引页面的这个特定请求中,已经有一个 3 深度的回调函数集。每个 callbuck 必须存在于“父级”的回调中,因为它取决于结果(在顺序执行的语言/环境中,这将是显而易见且微不足道的)。

我的问题是,在构建更复杂且可能非常大的应用程序时,如何避免大量嵌套回调函数的问题?当您对逻辑有顺序依赖时,当然会出现这种情况。我知道 Node 的理念是异步的,但是当谈到等待来自数据库的数据并说我们正在运行 5 个单独的查询时,那又如何呢?我们是否只需将单个多语句查询编写为原子单元?虽然这个问题并不是数据库独有的。

4

4 回答 4

2

由于您提到使用 Express,因此您可以使用 thenext()作为回调的替代方案。

app.get('/',function first(req,res,next){
   res.write('Hello ');
   res.locals.user = req.user;
   next();
   //Execute next handler in queue
});

app.get('/',function second(req,res,next){
   res.write('World!');
   //Use res.locals.user
   res.end();
});

//response shows Hello World!

路由处理程序使用额外的参数next并按照给定的顺序执行,直到其中一个返回响应。next根本不接受参数或错误作为参数。您可以设置要传递给下一个函数的变量res.locals

于 2013-03-24T04:28:15.617 回答
2

这里有一个关于这个问题的很好的一般性讨论:http: //callbackhell.com/

此外,许多人使用 Async 之类的模块来管理流控制问题。

于 2013-03-24T02:51:22.467 回答
1

One way that I am fond of doing is like this...

exports.index = function(req, res) {
  var connection

  db.getConnection(gotConnection)

  function gotConnection(err, _c) {
    connection = _c                                
    connection.query('SELECT * FROM test_table', gotData)
  }

  function gotData(err, rows) { 
    connection.end();        
    var templateVariables = {                                           
      title: 'Index Page', 
      response: rows[0].text     
    }
    res.render('index', templateVariables);
  }
});

You should also always handle errors as well in your code, I'm assuming you left them out to make the code easier to read here.

于 2013-03-24T03:47:29.923 回答
1

使用PromiseorFuture库,例如Q(在 npm 上可用)。

引用 Q 的自述文件,promises 可以让你改变这个:

step1(function (value1) {
    step2(value1, function(value2) {
        step3(value2, function(value3) {
            step4(value3, function(value4) {
                // Do something with value4
            });
        });
    });
});

进入这个:

Q.fcall(step1)
.then(step2)
.then(step3)
.then(step4)
.then(function (value4) {
    // Do something with value4
}, function (error) {
    // Handle any error from step1 through step4
})
.done();

我见过的所有其他回调地狱的解决方案都引入了似乎只是倒退的权衡。异步操作不会形成较大操作之间的自然逻辑边界,因此如果您分解函数或沿这些边界进行模块化,您将获得微分解代码。

于 2013-03-24T02:52:56.450 回答