我可以用什么来替换嵌套的异步回调?

时间:2013-01-04 01:38:02

标签: javascript node.js asynchronous callback middleware

假设我想发送电子邮件然后更新数据库,这两个操作都是异步的。这就是我通常会写它的方式。

send_email(function(err, id){
    if(err){
        console.log("error");
    }else{
        update_database(id,function(err, id){
            if(err){
                console.log("error");
            }else{
                console.log("success");
            }
        });
    }
});

我想用中间件来做这件事。

var mid = {};

mid.send_email = function(){
    return function(next){
        send_email(function(err,id){
            if(err){
                console.log("error");
            }else{
                next(id);
            }
        });
    }
}

mid.update_database = function(){
    return function(id,next){
        update_database(id,function(err,id){
            if(err){
                console.log("error");
            }else{
                next(id);
            }
        });
    }
}

mid.success = function(){
    return function(id,next){
        console.log("success")
        next(id);
    }   
}

堆叠中间件。

middleware.use(mid.send_email());
middleware.use(mid.update_database());
middleware.use(mid.success());

手头有两个主要问题。

  • 如何使用中间件代替嵌套回调?
  • 是否可以将变量传递给next()

3 个答案:

答案 0 :(得分:5)

您想要的是能够处理异步控制流程。很多js库可以帮助您实现这一目标。您可以使用Async函数尝试waterfall库,因为您希望能够将变量传递给将要执行的下一个函数:

https://github.com/caolan/async#waterfall

“运行一系列函数,每个函数都将它们的结果传递给数组中的下一个。但是,如果任何函数将错误传递给回调,则不执行下一个函数,并立即调用主回调错误。“

示例:

async.waterfall([
    function(callback){
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback){
        callback(null, 'three');
    },
    function(arg1, callback){
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
   // result now equals 'done'    
});

答案 1 :(得分:1)

最好使用CommonJS module.exports

您可以创建如下文件:

module.exports = function (){
    function sendEmail(doneCallback){
        // do your stuff, then when you are done:
        if(!err){
            doneCallback(whatever,args,you,need);
        }
    }

    function updateDB(success){
        // do your stuff, then when you are done:
        success(whatever,args,you,need);
    }

    return {
        send: sendEmail,
        update: updateDB
    };
};

然后在server.js

var lib = require('./mylib.js');

lib.send(function(result){
   console.log(result);
});

这是一个类似的模式,它可能会让你更好地了解我的意思。它由图书馆烘焙function并将其传递给任何需要连锁的人,就像这样(更多的是脚踏实地的例子,这次是客户端):

ui.bistate($('#mybutton'), function(restore){
    $.ajax({
        url: '/api/1.0/catfood',
        type: 'PUT',
        data: {
            catfood: {
                price: 1.23,
                name: 'cheap',
                text: 'Catzy'
            }
        }
    }).done(function(res){
        // stuff with res
        restore();
    });
});

并且在库中,这是restore的提供方式:

var ui = function(){
    function bistate(button, action) {
        var originalText = buttonText.data('text'),
            disabledText = buttonText.data('text-disabled');

        function restore(){
            button.prop('disabled', false);
            button.text(originalText);
        }

        function disable(){
            button.prop('disabled', true);
            button.text(disabledText);
        }

        button.on('click', function(){
            disable();
            action(restore);
        });
        restore();
    }

    return {
        bistate: bistate
    };
}();

允许消费者控制当他想要恢复按钮的流程,并重新使库不必处理消费者想要在其间进行异步操作的复杂情况。

一般来说,重点是:来回传递回调是巨大而不是足够广泛

答案 2 :(得分:0)

我一直在工作中使用Queue.js