循环内的异步函数完成后如何调用函数?

Posted

技术标签:

【中文标题】循环内的异步函数完成后如何调用函数?【英文标题】:How to call function after completion of async functions inside loop? 【发布时间】:2014-05-12 10:11:51 【问题描述】:

我在 NodeJS 中有一个 forEach 循环,遍历一系列键,然后从 Redis 异步检索这些键的值。循环和检索完成后,我想将该数据集作为响应返回。

我目前的问题是因为数据检索是异步的,发送响应时我的数组没有填充。

如何在我的 forEach 循环中使用 Promise 或回调来确保响应与数据一起发送?

exports.awesomeThings = function(req, res) 
    var things = [];
    client.lrange("awesomeThings", 0, -1, function(err, awesomeThings) 
        awesomeThings.forEach(function(awesomeThing) 
            client.hgetall("awesomething:"+awesomeThing, function(err, thing) 
                things.push(thing);
            )
        )
        console.log(things);
        return res.send(JSON.stringify(things));
    )

【问题讨论】:

这个问题有很多QA。有些列在此页面的右侧。 现在,我建议使用 Promise 库。 Here's an introduction @dystroy 您应该考虑为该问题添加一个承诺答案。现在的比较丑。此外,还有一些异步信号量。 【参考方案1】:

我在这里使用Bluebird promises。请注意代码的意图是多么清晰,并且没有嵌套。

首先,让我们promisify hgetall 调用和客户端 -

var client = Promise.promisifyAll(client);

现在,让我们编写带有 Promise 的代码,.then,而不是使用 .map 的节点回调和聚合。 .then 所做的是表示异步操作已完成。 .map 获取一系列事物并将它们全部映射到异步操作,就像您的 hgetall 调用一样。

注意 Bluebird 如何(默认情况下)向承诺的方法添加 Async 后缀。

exports.awesomeThings = function(req, res) 
    // make initial request, map the array - each element to a result
    return client.lrangeAsync("awesomeThings", 0, -1).map(function(awesomeThing) 
       return client.hgetallAsync("awesomething:" + awesomeThing);
    ).then(function(things) // all results ready 
         console.log(things); // log them
         res.send(JSON.stringify(things)); // send them
         return things; // so you can use from outside
    );
;

【讨论】:

谢谢,这是完美的。我知道承诺会是解决这个问题的方法,但是我发现的很多解决方案(自从发布这个问题以来)都非常……冗长。 很高兴我能帮上忙。 Promise 是一个非常强大的抽象,并且可以很好地简化代码。它们还内置了可调试性——如果上面的任何代码有错误——你会知道 Bluebird 的承诺。它们链接得很好,它们组合得很好,并且包含您实际需要的并发原语。从 EcmaScript 6 开始,它们也是 javascript 的一部分——下一个标准,因此可以公平地假设使用它们的代码非常兼容未来。【参考方案2】:

不需要库。很简单,它只是一个异步循环。省略了错误处理。如果您需要执行并行异步循环,请使用计数器。

exports.awesomeThings = function(req, res) 
    client.lrange("awesomeThings", 0, -1, function(err, awesomeThings) 
        var len = awesomeThings.length;
        var things = [];
        (function again (i)
            if (i === len)
                //End
                res.send(JSON.stringify(things));
            else
                client.hgetall("awesomething:"+awesomeThings[i], function(err, thing) 
                    things.push(thing);

                    //Next item
                    again (i + 1);
                )
            
        )(0);
);

【讨论】:

以上是关于循环内的异步函数完成后如何调用函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在库函数中使用异步事件循环

循环函数中的异步调用。仅在异步完成时返回

当for内部有查询时如何返回异步函数的值

如何设置计时器来控制循环内的函数调用?

当循环中调用了异步函数时,Node.JS 将如何处理循环控制?

在for循环中调用异步函数