我正在尝试在数组上运行一些数据库查询(使用 sails.js),并在查询返回时执行某些操作。我认为最好的方法是使用 for 循环并异步解决 promise ,一旦全部解决,就继续。但是,只有数组中的最后一个 promise 正在解析,并且它会解析多次,因为在每个“User.findOne...”then 函数中,索引是 array.length-1。
我的问题:
- 异步循环中的变量作用域如何工作?解释这一点的最佳资源?
- 解决我的问题的最佳方法是什么?为什么?
- 还有其他我应该使用或不应该使用的模式吗?我对 Promise 和异步 js 相当陌生,所以任何提示都会有帮助!
我检查过的主要教程
- https://github.com/kriskowal/q
- https://github.com/kriskowal/q/wiki/API-Reference
- https://github.com/bellbind/using-promise-q/
感谢您的帮助!
我的简化代码:
functionWhichReturnsPromise()
.then(function(user){
var promises = [];
Q.try(function(){
for (var index in array) {
var fbid = array[index];// Get fbid from array
promises.push(Q.defer().promise); // Add promise to promise array
// Find userid from fbid; resolve respective promise when finished
User.findOne({facebook_id: fbid}).then(function(userSeen){
promises[index].resolve(userSeen.id);
sails.log('resolved where id=' + userSeen.id); // correct
sails.log('resolved where index=' + index); // PROBLEM: always last index
});
}
}).then(function(){
// For debugging purposes
Q.delay(1000).then(function(){
sails.log(promises[0]); // Unresolved
sails.log(promises[1]); // Unresolved
sails.log(promises[2]); // Only last promise in array is resolved
});
// When the userids have been extracted from above (promises fulfilled)...
Q.all(promises).then(function(seenids){
// Do stuff here (Doesn't get here)
});
});
});
最佳答案
在 Javascript 中,变量的作用域是函数而不是大括号。
因此在下面的代码中,var index
的作用域不是for循环的大括号,作用域实际上是for循环所在的函数。
Q.try(function(){
for (var index in array) {
var fbid = array[index];// Get fbid from array
promises.push(Q.defer().promise); // Add promise to promise array
// Find userid from fbid; resolve respective promise when finished
User.findOne({facebook_id: fbid}).then(function(userSeen){
promises[index].resolve(userSeen.id);
sails.log('resolved where id=' + userSeen.id); // correct
sails.log('resolved where index=' + index); // PROBLEM: always last index
});
}
})
在 for 循环中,您调用异步函数,在您的情况下是 mongodb 调用 (findOne
)。
您应该始终假设这些异步函数可以花费任意毫秒数来运行(取决于函数)。但一般来说,循环通常会在异步函数运行之前完成。即使在这些函数开始运行之前,您的 for 循环也会触发所有这些异步函数。问题是所有待处理的异步函数仍然指向该变量index
。该变量对于所有变量都是通用的,因为 index
位于外部函数的范围内。
这是由于 Javascript 中的闭包而产生的问题。为了解决这个问题,我们需要使用更多的闭包。
您可以通过谷歌搜索有关闭包主题的许多资源。但请通过MDN's description of it .
如果您在循环内的另一个函数中捕获 index
的值,那么您就可以开始了。
这是我针对您的问题建议的解决方案。虽然我还没有测试过,但你明白了。
Q.try (function () {
array.forEach( function(ele, idx, array) {
(function(index) {
var fbid = array[index]; // Get fbid from array
promises.push(Q.defer().promise); // Add promise to promise array
// Find userid from fbid; resolve respective promise when finished
User.findOne({
facebook_id : fbid
}).then(function (userSeen) {
promises[index].resolve(userSeen.id);
sails.log('resolved where id=' + userSeen.id); // correct
sails.log('resolved where index=' + index); // PROBLEM: always last index
});
})(idx);
})
})
希望这有帮助。
另请注意: it is incorrect to use for...in for iterating through arrays .
关于node.js - 异步 Node.js 循环中的变量作用域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31196309/