【问题标题】:NodeJS: invoking callback function inside a for loopNodeJS:在for循环中调用回调函数
【发布时间】:2013-11-21 09:00:58
【问题描述】:

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

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*/
       });
    });
}  

我在我的应用程序的多个位置有相同的模式,有时我遇到了回调问题,for 循环结束,但回调没有给出响应,这似乎是 DAO 方法已被调用但仍在等待回复。 有没有优化的方法来克服这个问题?

很高兴知道是否有一些 javascript 编码模式可以解决这个问题。(除了任何 3rd 方库)谢谢

【问题讨论】:

    标签: javascript node.js callback


    【解决方案1】:

    您听说过延迟或承诺吗?我想这就是你要找的。在它的基本形式中,它基本上是一个有两个处理程序的对象。一个代表失败,一个代表成功。

    但还有其他帮助函数,例如 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

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

    当您获得概念后,自己实施它应该不会太难。

    【讨论】:

    • Deferreds 或 Promises 我听说过它们,但我无法用我的代码实现。有什么好的资源/电子书可以用来解决我的问题吗?
    • @Aman Async JavaScript 一书。它还谈到了保罗提到的瀑布。您应该将您的 DAO 函数重写为实际的返回承诺并将您的处理程序附加到它们。然后你就可以避免这种讨厌的嵌套。
    • 我觉得这有点乏味(nodejs 的初学者)对此感到抱歉,但您能否分享一些代码作为使用 Deferreds 或 Promises 的示例?这将是一件好事,基本上我不想去任何 3rd 方库,想以自己编写的方式实现它.. plz。谢谢
    • @Aman - 查看工作示例。如果您不想使用成品,请自行编写。自己编写基本的完成/失败处理并不难。
    【解决方案2】:

    您可以使用 async.waterfall function 来解决您的问题。所有函数都被串行调用,函数的结果作为参数发送给下一个函数。以下是文档中的示例用法:

    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'    
    });
    

    【讨论】:

      【解决方案3】:

      看看这段代码。

      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);
      

      这是你需要的吗?

      【讨论】:

      • no no ...我已经实现了这个,基本上我想知道为什么回调挂起?以及如何避免嵌套回调。
      • @Gowsikan,你怎么知道getData(i) 是否完成并返回了一个值?事情是一些时间回调不会根据分配的任务或nodejs线程的优先级立即给你输出(如果我没有错的话)。
      • @Aman 返回一个 promise 并在到达内部循环中的 else 分支时解决延迟。
      • 这个方法只是在得到previous的响应后才调用next方法(可能成功也可能失败)。如果你放一个 for 循环,循环将在你得到响应之前结束。
      猜你喜欢
      • 2021-05-28
      • 1970-01-01
      • 2018-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-02
      • 2013-12-11
      相关资源
      最近更新 更多