首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Node.js进行同步数据库查询

使用Node.js进行同步数据库查询
EN

Stack Overflow用户
提问于 2011-07-06 21:50:00
回答 6查看 95.4K关注 0票数 57

我有一个Node.js/Express应用程序,它可以查询路由中的MySQL数据库,并将结果显示给用户。我的问题是,在将用户重定向到他们请求的页面之前,我如何运行查询并阻塞,直到两个查询都完成?

在我的示例中,在呈现页面之前,我有两个查询需要完成。如果我将查询2嵌套在查询1的“result”回调中,我就可以让查询同步运行。然而,当查询的数量增加时,这将变得非常复杂。

如何在不将后续查询嵌套在前一个查询的“result”回调中的情况下,同步运行多个(在本例中为2个)数据库查询?

我已经查看了Node模块中的“flow control / Async”,并尝试了flow-js,但我无法让它与异步查询一起工作。

下面列出了我试图从'/home‘路径执行的两个查询。Node专家能否解释这样做的“正确”方法。

代码语言:javascript
复制
app.get('/home', function (req,res) {
    var user_array = [];
    var title_array = [];

    // first query
    var sql = 'select user_name from users';
    db.execute(sql)
        .addListener('row', function(r) {
            user_array.push( { user_name: r.user_name } );
        })
        .addListener('result', function(r) {
            req.session.user_array = user_array;
        });

    // second query
    var sql = 'select title from code_samples';
    db.execute(sql)
        .addListener('row', function(r) {
            title_array.push( { title: r.title } );
        })
        .addListener('result', function(r) {
            req.session.title_array = title_array;
        });

        // because the queries are async no data is returned to the user
        res.render('home.ejs', {layout: false, locals: { user_name: user_array, title: title_array }});
});
EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2011-07-07 06:58:25

node的目标不是关心事情发生的顺序。这可能会使一些场景变得复杂。嵌套回调没有什么丢人的。一旦你习惯了它的外观,你可能会发现你实际上更喜欢这种风格。是的,很清楚会触发什么样的订单回调。如果有必要,您可以放弃匿名函数,使其不那么冗长。

如果你愿意稍微重构一下你的代码,你可以使用“典型的”嵌套回调方法。如果你想避免回调,有很多异步框架可以帮助你做到这一点。您可能想要检查的一个是async.js (https://github.com/fjakobs/async.js)。每种方法的示例:

代码语言:javascript
复制
app.get('/home', function (req,res) {
    var lock = 2;
    var result = {};
    result.user_array = [];
    result.title_array = [];

    var finishRequest = function(result) {
        req.session.title_array = result.title_array;
        req.session.user_array = result.user_array;
        res.render('home.ejs', {layout: false, locals: { user_name: result.user_array, title: result.title_array }});
    };

    // first query
    var q1 = function(fn) {
      var sql = 'select user_name from users';
      db.execute(sql)
          .addListener('row', function(r) {
              result.user_array.push( { user_name: r.user_name } );
          })
          .addListener('result', function(r) {
              return fn && fn(null, result);
        });
    };

    // second query
    var q2 = function(fn) {
      var sql = 'select title from code_samples';
      db.execute(sql)
          .addListener('row', function(r) {
              result.title_array.push( { title: r.title } );
          })
          .addListener('result', function(r) {
              return fn && fn(null, result);
          });
    }

    //Standard nested callbacks
    q1(function (err, result) {
      if (err) { return; //do something}

      q2(function (err, result) {
        if (err) { return; //do something}

        finishRequest(result);
      });
    });

    //Using async.js
    async.list([
        q1,
        q2,
    ]).call().end(function(err, result) {
      finishRequest(result);
    });

});

对于一次性的,我可能只会使用引用计数类型的方法。只需跟踪要执行的查询数,并在所有查询都完成后呈现响应。

代码语言:javascript
复制
app.get('/home', function (req,res) {
    var lock = 2;
    var user_array = [];
    var title_array = [];

    var finishRequest = function() {
        res.render('home.ejs', {layout: false, locals: { user_name: user_array, title: title_array }});
    }

    // first query
    var sql = 'select user_name from users';
    db.execute(sql)
        .addListener('row', function(r) {
            user_array.push( { user_name: r.user_name } );
        })
        .addListener('result', function(r) {
            req.session.user_array = user_array;
            lock -= 1;

            if (lock === 0) {
              finishRequest();
            }
        });

    // second query
    var sql = 'select title from code_samples';
    db.execute(sql)
        .addListener('row', function(r) {
            title_array.push( { title: r.title } );
        })
        .addListener('result', function(r) {
            req.session.title_array = title_array;
            lock -= 1;

            if (lock === 0) {
              finishRequest();
            }
        });
});

一种更好的方法是在呈现响应之前,在每个“结果”回调中调用finishRequest()来检查非空数组。这是否适用于您的情况取决于您的需求。

票数 56
EN

Stack Overflow用户

发布于 2011-07-08 12:51:02

这里有一个非常简单的技巧来处理多个回调。

代码语言:javascript
复制
var after = function _after(count, f) {
  var c = 0, results = [];
  return function _callback() {
    switch (arguments.length) {
      case 0: results.push(null); break;
      case 1: results.push(arguments[0]); break;
      default: results.push(Array.prototype.slice.call(arguments)); break;
    }
    if (++c === count) {
      f.apply(this, results);
    }
  };
};

Example

使用:

代码语言:javascript
复制
var handleDatabase = after(2, function (res1, res2) {
  res.render('home.ejs', { locals: { r1: res1, r2: res2 }):
})

db.execute(sql1).on('result', handleDatabase);
db.execute(sql2).on('result', handleDatabase);

所以基本上你需要引用计数。这是这些情况下的标准方法。我实际上使用的是这个小实用函数,而不是流控制。

如果你想要一个完整的流量控制解决方案,我会推荐futuresJS

票数 18
EN

Stack Overflow用户

发布于 2011-07-11 10:43:22

我发现对于这样的事情,异步库是最好的。https://github.com/caolan/async#parallel

我不能测试这个或任何东西,所以如果有一些打字错误,请原谅我。我重构了你的查询函数,使其可重用。因此,调用queryRows将返回一个与异步模块的并行回调函数的格式匹配的函数。在两个查询完成后,它将调用last函数并将两个查询的结果作为参数传递,您可以读取该参数以将其传递给模板。

代码语言:javascript
复制
function queryRows(col, table) {
  return function(cb) {
    var rows = [];
    db.execute('SELECT ' + col + ' FROM ' + table)
      .on('row', function(r) {
        rows.push(r)        
      })
      .on('result', function() {
        cb(rows);
      });
  }
}

app.get('/home', function(req, res) {
  async.parallel({
    users: queryRow('user_name', 'users'),
    titles: queryRow('title', 'code_samples')
  },
  function(result) {
    res.render('home.ejs', { 
      layout: false,
      locals: {user_name: result.users, title: result.titles} 
    });
  });
});
票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6597493

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档