繁体   English   中英

在nodeJs中避免回调地狱/将变量传递给内部函数

[英]Avoiding callback hell in nodeJs / Passing variables to inner functions

这是我想简化的一个例子:

exports.generateUrl = function (req, res) {
    var id = req.query.someParameter;

    var query = MyMongooseModel.findOne({'id': id});
    query.exec(function (err, mongooseModel) {
        if(err) {
            //deal with it
        }

        if (!mongooseModel) {
            generateUrl(Id,
                function (err, text, url) {
                    if (err) {
                        res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
                        return;
                    }
                    var newMongooseModel = new AnotherMongooseModel();
                    newMongooseModel.id = id;

                    newMongooseModel.save(function (err) {
                        if (err) {
                            res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
                        } else {
                            res.send({url: url, text: text});
                        }
                    });
                });
        } else {
            //deal with already exists
        }
    });
};

我已经看到了其他SO的答案,他们告诉你使用命名函数,但是没有说明如何处理你想要传入的变量或者使用jQuery的队列。 我也没有奢侈品。

我知道我可以用名称函数替换我的匿名函数,但是我需要传递变量。 如果函数在别处定义,我的内部函数如何访问res

你问题的核心是:

我知道我可以用名称函数替换我的匿名函数,但是我需要传递变量。 如果函数在别处定义,我的内部函数如何访问res?

答案是使用功能工厂。

一般来说,这个:

function x (a) {
    do_something(function(){
        process(a);
    });
}

可以转换为:

function x (a) {
    do_something(y_maker(a)); // notice we're calling y_maker,
                              // not passing it in as callback
}

function y_maker (b) {
    return function () {
        process(b);
    };
}

在上面的代码中, y_maker是一个生成函数的函数(让我们调用该函数的目的“y”)。 在我自己的代码中,我使用命名约定.._makergenerate_..来表示我正在调用函数工厂。 但这只是我而且这个惯例绝不是标准的,也不是广泛采用的。

因此,对于您的代码,您可以将其重构为:

exports.generateUrl = function (req, res) {
    var id = req.query.someParameter;

    var query = MyMongooseModel.findOne({'id': id});
    query.exec(make_queryHandler(req,res));
};

function make_queryHandler (req, res) {
    return function (err, mongooseModel) {
        if(err) {
            //deal with it
        }
        else if (!mongooseModel) {
            generateUrl(Id,make_urlGeneratorHandler(req,res));
        } else {
            //deal with already exists
        }
}}

function make_urlGeneratorHandler (req, res) {
    return function (err, text, url) {
        if (err) {
            res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
            return;
        }
        var newMongooseModel = new AnotherMongooseModel();
        newMongooseModel.id = id;
        newMongooseModel.save(make_modelSaveHandler(req,res));
}}

function make_modelSaveHandler (req, res) {
    return function (err) {
        if (err) res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
        else res.send({url: url, text: text});
}}

这使得嵌套回调变得扁平化。 作为额外的好处,您可以正确命名函数应该执行的操作。 我认为这是好的做法。

它还有一个额外的好处,它比使用匿名回调(使用嵌套回调或使用promises)快得多,但如果你将命名函数传递给promise.then()而不是匿名函数,那么你将获得相同的加速福利)。 之前的SO问题(我的google-fu今天让我失败了)发现命名函数的速度是node.js中匿名函数的两倍以上(如果我没记错的话,速度提高了5倍多)。

使用承诺。 使用Q和mongoose-q它会给出:类似的东西:

exports.generateUrl = function (req, res) {
    var id = req.query.someParameter;
    var text = "";

    var query = MyMongooseModel.findOne({'id': id});
    query.execQ().then(function (mongooseModel) {

        if (!mongooseModel) {
            return generateUrl(Id)

     }).then(function (text) {
       var newMongooseModel = new AnotherMongooseModel();
       newMongooseModel.id = id;
       text = text;

       newMongooseModel.saveQ()
     }).then(function (url) {
        res.send({url: url, text: text});
     }).fail(function(err) {
        res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
     });
};

命名函数将在匿名函数所在的范围内执行,并且可以访问您当前使用的所有变量。 这种方法可以使你的代码更少嵌套,更易读(这很好),但技术上仍然是“回调地狱”。 避免这种情况的最好方法是使用像Q这样的promise库来包装异步库(假设它们还没有提供promises)。 IMO,承诺提供了更加清晰的执行路径图。

通过使用bind参数绑定到命名函数,可以避免不知道变量来自何处的困境,例如:

function handleRequest(res, err, text, url) {
    if (err) {
        res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
        return;
    }
    var newMongooseModel = new AnotherMongooseModel();
    newMongooseModel.id = id;

    newMongooseModel.save(function (err) {
        if (err) {
            res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
        } else {
            res.send({url: url, text: text});
        }
    });
}

...
generateUrl(Id, handleRequest.bind(null, res));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM