Javascript forEach 非阻塞查询
Javascript forEach non blocking query
在我的控制器 (sails.js) 中,我有一个包含 ID 列表的数组。
这些是另一个 table 的 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);
}
行得通,但看起来像是作弊。在我看来,这是阻塞代码,而不是 acceptable。在这种情况下有没有通常的处理方式?
如果 findOne
异步完成,而且看起来确实如此,那么 forEach
可能会阻塞几分之一毫秒。 以后、findOne
将在每个查找完成时调用每个回调。从阻塞的角度来看,该代码很好,前提是 findOne
异步完成。
但是,代码有一个不同的问题:你假设回调将按顺序发生,方法是:
if (index === array.length - 1) {
completeSend(suggesterResults);
}
除非 findOne
记录它,否则您不能做出该假设(我查看了 sails.js 网站;除了没有说什么的项目符号列表)。回调可能会乱序到达,例如,如果一次查找比之前的查找快。
相反,您需要跟踪您收到了多少 次回叫,并在收到与请求相同的电话号码时调用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
稀疏的可能性,这似乎不太可能。)
承诺:
Promise.all(suggestions.map(function(suggestion) {
return Suggester.findOne({"id": suggestion.suggester_id});
})
.then(completeSend);
让我们解释每一行:
<a href="https://github.com/petkaantonov/bluebird/blob/master/API.md#all---promise" rel="nofollow">Promise.all</a>(suggestions.map(function(suggestion) {
return Suggester.findOne({"id": suggestion.suggester_id});
})
将建议数组映射到新的承诺数组中。 findOne
returns 对未来查询结果的承诺。
Promise.all()
是一个静态方法,它接受一个承诺数组,returns 一个单一的承诺,当数组中的所有承诺都成功解析时解析。该承诺 以原始顺序 的所有已解析值的数组解析,这恰好正是您想要的。
.then(completeSend);
这个 .then
被调用的承诺是从 Promise.all()
返回的承诺,所以它等同于用所有 docs
项的数组调用 completeSend()
,之后所有的承诺都已兑现。
正确的做法是
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);
在我的控制器 (sails.js) 中,我有一个包含 ID 列表的数组。
这些是另一个 table 的 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);
}
行得通,但看起来像是作弊。在我看来,这是阻塞代码,而不是 acceptable。在这种情况下有没有通常的处理方式?
如果 findOne
异步完成,而且看起来确实如此,那么 forEach
可能会阻塞几分之一毫秒。 以后、findOne
将在每个查找完成时调用每个回调。从阻塞的角度来看,该代码很好,前提是 findOne
异步完成。
但是,代码有一个不同的问题:你假设回调将按顺序发生,方法是:
if (index === array.length - 1) {
completeSend(suggesterResults);
}
除非 findOne
记录它,否则您不能做出该假设(我查看了 sails.js 网站;除了没有说什么的项目符号列表)。回调可能会乱序到达,例如,如果一次查找比之前的查找快。
相反,您需要跟踪您收到了多少 次回叫,并在收到与请求相同的电话号码时调用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
稀疏的可能性,这似乎不太可能。)
承诺:
Promise.all(suggestions.map(function(suggestion) {
return Suggester.findOne({"id": suggestion.suggester_id});
})
.then(completeSend);
让我们解释每一行:
<a href="https://github.com/petkaantonov/bluebird/blob/master/API.md#all---promise" rel="nofollow">Promise.all</a>(suggestions.map(function(suggestion) {
return Suggester.findOne({"id": suggestion.suggester_id});
})
将建议数组映射到新的承诺数组中。 findOne
returns 对未来查询结果的承诺。
Promise.all()
是一个静态方法,它接受一个承诺数组,returns 一个单一的承诺,当数组中的所有承诺都成功解析时解析。该承诺 以原始顺序 的所有已解析值的数组解析,这恰好正是您想要的。
.then(completeSend);
这个 .then
被调用的承诺是从 Promise.all()
返回的承诺,所以它等同于用所有 docs
项的数组调用 completeSend()
,之后所有的承诺都已兑现。
正确的做法是
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);