Javascript forEach非阻塞查询

时间:2015-01-27 08:59:12

标签: javascript sails.js

在我的控制器(sails.js)中,我有一个数组,其中包含一个ID列表。

这些是来自另一个表的ID。我需要使用该ID提取每条记录,并将其发送给用户。

到目前为止,我已经制作了这段代码:

     suggestions.forEach(function (element, index, array){
                    Suggester.findOne({
                    "id": element.suggester_id
                    },function(err,docs){
                        suggesterResults.push(docs);
                        console.log("I am adding to array: " + docs);
                        if (index === array.length - 1) {
                            completeSend(suggesterResults);
                        }
                    });
                })
    ...
    function completeSend (results) {
        console.log("I am in complete send method"  + results)
        return res.send(results, 200);
    }

这有效,但看起来像是作弊。在我看来这是阻止代码,这是不可接受的。在这种情况下是否有通常的做事方式?

3 个答案:

答案 0 :(得分:3)

正确的方法是

Suggester.find({
    id: suggestions.map(function(s) { return s.suggester_id; })
})
.then(completeSend);

您拨打find一次,并传递一系列ID,一旦完成,completeSend将被调用结果数组。


您提到所有这些已经成为承诺链的一部分,在这种情况下,.then()有不良做法(不要嵌套.then()来电!)

如果是这样,那么正确的方法是:

// Some promise chain logic here
.then(function(/* suggestions? */) {
    return Suggester.find({
        id: suggestions.map(function(s) { return s.suggester_id; })
    });
})
.then(completeSend);

答案 1 :(得分:2)

承诺:

Promise.all(suggestions.map(function(suggestion) {
    return Suggester.findOne({"id": suggestion.suggester_id});
})
.then(completeSend);

让我们解释每一行:

Promise.all(suggestions.map(function(suggestion) {
    return Suggester.findOne({"id": suggestion.suggester_id});
})

将suggest数组映射到一个新的promises数组中。 findOne返回包含查询未来结果的承诺。

Promise.all()是一个静态方法,它接受一个promise数组,并返回一个单一的promise,当数组中的所有promise都成功解析时,它会解析。该承诺使用原始顺序中的所有已解析值的数组解析,这恰好正是您想要的。

.then(completeSend);

调用此.then的承诺是从Promise.all()返回的承诺,因此它等同于使用所有completeSend()项的数组调用docs承诺得到了解决。

答案 2 :(得分:1)

如果findOne异步完成,并且看起来确实如此,那么forEach将阻塞可能只有几分之一毫秒。 稍后findOne会在每次查询完成时调用每个回调。从阻塞的角度来看,考虑到findOne异步完成的条件,该代码很好。

但是,代码有一个不同的问题:你假设回调将按顺序发生,通过这样做:

if (index === array.length - 1) {
    completeSend(suggesterResults);
}

你不能做出这个假设,除非findOne记录它(我查看了sails.js网站;找不到findOne的任何文档而不是一个条目没有说什么的子弹清单。回调可能无序到达,例如,如果一次查找比之前的查找快一些。

相反,您希望跟踪您已收到的回复次数,并在您获得与请求相同的号码时致电completeSend你做了,而不是依赖索引。

如果您在启动时suggesterResults为空,并且在呼叫未完成时无法修改suggestions,则可以使用其length

suggestions.forEach(function (element, index, array){
    Suggester.findOne({
    "id": element.suggester_id
    },function(err,docs){
        suggesterResults.push(docs);
        console.log("I am adding to array: " + docs);
        if (suggesterResults.length === array.length) {
            completeSend(suggesterResults);
        }
    });
})

但如果其中任何一个警告都不对,那么你最好还是使用一个柜台:

var waitingon = 0;
suggestions.forEach(function (element, index, array){
    ++waitingon;
    Suggester.findOne({
    "id": element.suggester_id
    },function(err,docs){
        suggesterResults.push(docs);
        console.log("I am adding to array: " + docs);
        if (--waitingon === 0) {
            completeSend(suggesterResults);
        }
    });
})

看起来像竞争条件,但它不是因为这是一个单线程环境。所有forEach回调都会在第一次findOne回调之前发生,因此waitingon会在我们开始递减之前上升到适当的级别。 (这也允许suggestions稀疏的可能性,这似乎不太可能。)