NodeJS:在for循环中调用回调函数

5

我正在尝试调用一个函数,其中一个循环运行,其中存在许多回调函数(回调地狱)..如下所示:

for(var i=0;i<data.id.length;i++)
{
    DAO.getUserById(data.id[i],function(err,resp1)
    {
       /* some other work based on resp1 */
       DAO.getOtherData(resp1.username,resp1.userId,function(err,resp2)
       {
           /* similary some other work*/
       });
    });
}  

我的应用程序中有几个地方都使用了相同的模式,有时候我会遇到回调函数出现问题的情况。循环已经结束了,但是回调函数仍然没有响应。这似乎是DAO方法已被调用,但仍在等待响应。

有没有一种优化的方法来解决这个问题呢?除了任何第三方库之外,是否有可用的JavaScript编码模式可以解决这个问题。谢谢。

3个回答

3

你听说过延迟对象或承诺吗?我认为这就是你要找的东西。在基本形式中,它基本上是一个具有两个处理程序的对象。一个用于失败,一个用于成功。

但还有其他帮助函数,如thenwhen,它们让您以更可读的方式链接函数。看看q或jQuery实现。对于非常好的介绍,请阅读Async JavaScript书籍。

编辑:

我为您做了一个小的工作示例,js fiddle

    var data = { id : [] };

    for(var i = 0; i < 10; i++) {
        data.id.push(i);
    }

    // DAO definition
    var DAO = {
        getUserById : function(id) {
            var deferred = $.Deferred();

            setTimeout(function() { 
                var isError = Math.floor(Math.random()*11) > 5;

                if(isError) {
                    deferred.reject("WOW - Much Handler - So error");
                } else {
                    deferred.resolve({
                        username :  'Max',
                        userId : id
                    }); 
                }
            }, 50);

            return deferred.promise();
        },

        getOtherData : function(username, userId) {
            var deferred = $.Deferred();

            setTimeout(function() {
                deferred.resolve((username + ' id: ' + userId));
            }, 20);

            return deferred.promise();
        }
    };



    function printResult(res) {
        $('#result').html($('#result').html() + '<br />' + res);
    };

    // DAO usage

    for(var i=0;i<data.id.length;i++)
    {
        DAO.getUserById(data.id[i])
        .done(function(res) {
            DAO.getOtherData(res.username, res.userId).done(function(result) {
                printResult(result);
            });
        })
        .fail(function(res) {
            printResult(res);
        });
    }

这样做的巨大优势有两个:

  1. 您可以免费获得错误处理程序代码和结果处理程序代码的分离
  2. 它可以防止您陷入嵌套地狱(回调中的回调,回调中的回调...)。由于延迟,您能够将实际逻辑分离出来。
  3. 回调的同步非常容易,因为您只需要使用when

我使用了jQuery的Deferreds,因为jsFiddle在下拉框中有jquery。您可以使用任何实现。

一旦理解概念,自己实现就不会太难。


延迟对象(Deferreds)或承诺(Promises) 我听说过它们,但是我无法在我的代码中实现它们。是否有任何好的资源/电子书可用,我可以使用它来解决我的问题? - Aman Gupta
@Aman 《Async JavaScript》这本书也提到了Paul所说的waterfall。你应该重写DAO函数以实际返回promises并将处理程序附加到它们上面。然后你就可以避免这种令人讨厌的嵌套了。 - schlingel
我发现这有点繁琐(我是Node.js的初学者),很抱歉,但您能否分享一些用Deferred或Promises的代码示例?这将是一件好事,基本上我不想使用任何第三方库,想要自己编写实现。。拜托了。谢谢 - Aman Gupta
@阿曼 - 请查看工作示例。如果您不想使用成品,请自己编写代码。基本的成功/失败处理不难编写。 - schlingel

2
您可以使用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'    
});

0

看一下这段代码。

var i=0;
var length = data.id.length;
var getData = function(index)
{
    DAO.getUserById(data.id[index],function(err,resp1)
    {
        /* some other work based on resp1 */
        DAO.getOtherData(resp1.username,resp1.userId,function(err,resp2)
        {
             /* similary some other work*/
             index++;
             if(index < length)
             {
                getData(index);
             }
        });
   });
}
getData(i);

这是你需要的吗?


不不...我已经实现了这个,基本上我想知道为什么回调挂起?以及如何避免嵌套回调。 - Aman Gupta
@Gowsikan,你怎么知道getData(i)已经完成并返回了一个值?问题是有时回调函数不会立即根据分配的任务或Node.js线程的优先级给出输出(如果我没有记错的话)。 - Aman Gupta
@Aman 当你到达内部循环的else分支时,返回一个Promise并解决延迟。 - schlingel
这种方法仅在获取先前的响应(可能是成功或失败)后才调用下一个方法。如果您放置一个for循环,那么循环将在您获得响应之前结束。 - Gowsikan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接