【问题标题】:asynchronous javascript best practices异步 JavaScript 最佳实践
【发布时间】:2013-12-26 22:24:09
【问题描述】:

使用 node.js/express.js,下面是我在处理异步代码时遇到的一个典型问题的示例(函数通过 2 db 调用在请求上渲染页面):

exports.index = function(req, res){
    res.render('index', {
        data1: getData1(),
        data2: getData2()
    });
};

这是一个函数的样子:

function getData1() {
    var assets = db.get("assets");
    assets.find().on('success', function (docs) {
        // What to do ?
    });
}

我尝试使用 async.js,这是一个非常有用的模块,但我仍然缺少有关如何处理此类案例的知识。

有人可以推荐关于异步 javascript 编码最佳实践的良好资源吗?

【问题讨论】:

标签: javascript asynchronous express


【解决方案1】:

使用异步工作流库,例如async

function getData1(done) {
    var assets = db.get('assets');
    assets.find().on('success', done);
}

function getData2(done) {
    var assets2 = db.get('assets2');
    assets2.find().on('success', done);
}

exports.index = function(req, res, next){
    async.parallel({
        d1: getData1,
        d2: getData2
    }, render);

    function render (err, data) {
        if (err) {
            return next(err);
        }

        res.render('index', {
            data1: data.d1,
            data2: data.d2
        });
    });
};

通过这种方式,您可以坚持在整个 Node 包中使用的 callback(err, result) 样式,同时保持您的代码简洁美观。 async 提供了简化工作流程的方法。在这种情况下,我使用了async.parallel 方法,该方法允许您同时运行任意数量的回调,然后在其他回调完成时运行作为第二个参数提供的函数。

【讨论】:

  • 谢谢 Nico,我喜欢异步的方法。这是否意味着在 getData 我必须调用 assets.find().on('success', done(err, queyrresult)); ?如果是这样,它不会使函数 getData 真正可重用,对吧?
  • 您可以只传递done 回调,如.on('success', done),因为它具有您描述的(err, result) 签名。它们是完全可重用的,因为您可以在需要执行 getData12 的其他上下文中使用这些相同的查询
【解决方案2】:

解决问题的一种方法是通过将回调函数传递给执行异步操作的函数来链接异步命令:

exports.index = function(req, res){
    getData1(function (data1) {
        getData2(function (data2) {
            res.render('index', {
                data1: data1,
                data2: data2
            });
        });
    });
};

function getData1(callback) {
    var assets = db.get("assets");
    assets.find().on('success', callback);
}
// Similarly for getData2

但是,嵌套回调很快就会变得难以管理。您可以命名函数来帮助解决这个问题:

exports.index = function(req, res){
    var data2Success = function (data1, data2) {
        res.render('index', {
            data1: data1,
            data2: data2
        });
    },
    data1Success = function (data1) {
        getData2(data2Success.bind(this, data1));
    };

    getData1(data1Success);
};

【讨论】:

    【解决方案3】:

    我通常会做以下事情之一:

    1) 嵌套调用:

    exports.index = function(req, res){
      //first call
      assets.getRecord(function (err, docs) {
    
        // 2nd call
        assets.getSomeOtherRecord(function (err, otherDocs) {
            res.render('index', {
              data1:docs,
              data2: otherDocs
            });
        );
    
      });
    
    };
    

    如果您的代码很简单,并且您不介意一个接一个地运行这些代码,那么这就足够了。如果这些不适用,我使用方法#2:

    2) 使用 async.js 或 Q 作为此处显示的其他答案。

    学习异步的资源:
    - http://net.tutsplus.com/tutorials/javascript-ajax/managing-the-asynchronous-nature-of-node-js/
    - http://www.sebastianseilund.com/nodejs-async-in-practice

    【讨论】:

    • 感谢 Jamis,1) 绝对不是我想要采用的方式,因为我的两个数据库查询根本无法重用。感谢您的资源!
    猜你喜欢
    • 2010-10-09
    • 2011-08-12
    • 2013-02-02
    • 1970-01-01
    • 2018-12-26
    • 2018-11-24
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多