- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我是 nodejs 的新手,正在尝试理解它的异步思想。在下面的代码片段中,我试图从 mongodb 数据库中随机获取两个文档。它工作正常,但由于嵌套的回调函数看起来非常难看。如果我想获取 100 个文档而不是 2 个,那将是一场灾难。
app.get('/api/two', function(req, res){
dataset.count(function(err, count){
var docs = [];
var rand = Math.floor(Math.random() * count);
dataset.findOne({'index':rand}, function(err, doc){
docs.push(doc);
rand = Math.floor(Math.random() * count);
dataset.findOne({'index':rand}, function(err, doc1){
docs.push(doc1);
res.json(docs);
});
});
});
});
所以我尝试使用 for-loop 代替,但是,下面的代码不起作用,我想我误解了异步方法的想法。
app.get('/api/two', function(req, res){
dataset.count(function(err, count){
var docs = []
for(i = 0; i < 2 ; i++){
var rand = Math.floor(Math.random() * count);
dataset.findOne({'index':rand}, function(err, doc){
docs.push(doc);
});
}
res.json(docs);
});
});
任何人都可以帮助我并向我解释为什么它不起作用吗?非常感谢。
最佳答案
谁能帮我解决这个问题并向我解释为什么它不起作用?
tl;dr -- 问题是由在循环完成之前无法完成的异步函数 (dataset.findOne
) 上运行循环引起的。您需要使用像 async
这样的库(如其他答案所建议的那样)或通过第一个代码示例中的回调来处理此问题。
循环同步函数
这可能听起来很迂腐,但重要的是要了解同步和异步世界中循环之间的区别。考虑这个同步循环:
var numbers = [];
for( i = 0 ; i < 5 ; i++ ){
numbers[i] = i*2;
}
console.log("array:",numbers);
在我的系统上,输出:
array: [ 0, 2, 4, 6, 8 ]
这是因为对 numbers[i]
的赋值发生在循环可以迭代之前。对于任何同步(“阻塞”)赋值/函数,您将以这种方式获得结果。
为了说明,让我们试试这段代码:
function sleep(time){
var stop = new Date().getTime();
while(new Date().getTime() < stop + time) {}
}
for( i = 0 ; i < 5 ; i++ ){
sleep(1000);
}
如果您留意或输入一些 console.log
消息,您会看到它“休眠”了 5 秒。
这是因为 sleep
中的 while
循环 block ......它迭代直到 time
毫秒过去,然后才将控制权返回给for 循环。
循环异步函数
问题的根源在于 dataset.findOne
是异步的...这意味着它在数据库返回结果之前将控制权传递回循环。 findOne
方法采用创建闭包的回调(匿名 function(err, doc)
)。
在这里描述闭包超出了这个答案的范围,但是如果你搜索这个网站或使用你最喜欢的搜索引擎搜索“javascript 闭包”,你会得到大量的信息。
不过,底线是异步调用将查询发送到数据库。因为事务需要一些时间并且它有一个可以接受查询结果的回调,所以它将控制权交还给 for 循环。 (重要:这就是 Node 的“事件循环”及其与“异步编程”的交叉点发挥作用的地方。 Node 通过允许这样的异步行为来提供非阻塞环境。)
让我们看一个例子,说明异步问题是如何让我们绊倒的:
for( i = 0 ; i < 5 ; i++ ){
setTimeout(
function(){console.log("I think I is: ", i);} // anonymous callback
,1 // wait 1ms before using the callback function
)
}
console.log("I am done executing.")
你会得到如下所示的输出:
I am done executing.
I think I is: 5
I think I is: 5
I think I is: 5
I think I is: 5
I think I is: 5
这是因为 setTimeout
获取一个函数来调用...所以即使我们只说“等待一毫秒”,这仍然比循环迭代 5 次并继续前进所花费的时间长到最后的 console.log
行。
然后,最后一行在第一个匿名回调触发之前 触发。当它确实触发时,循环结束并且i
等于5
。所以你在这里看到的是循环已经完成,继续前进,即使传递给 setTimeout
的匿名函数仍然可以访问 i
的值。 (这是实际的“关闭”...)
如果我们采用这个概念并将其用于考虑您的第二个“损坏的”代码示例,我们可以看出您没有获得预期结果的原因。
app.get('/api/two', function(req, res){
dataset.count(function(err, count){
var docs = []
for(i = 0; i < 2 ; i++){
var rand = Math.floor(Math.random() * count);
// THIS IS ASYNCHRONOUS.
// findOne gets a callback...
// hands control back to the for loop...
// and later pushes info into the "doc" array...
// too late for res.json, at least...
dataset.findOne({'index':rand}, function(err, doc){
docs.push(doc);
});
}
// THE LOOP HAS ENDED BEFORE any of the findOne callbacks fire...
// There's nothing in 'docs' to be sent back to the client. :(
res.json(docs);
});
});
之所以 async
、promises 和其他类似的库是一个很好的工具,是因为它们有助于解决您面临的问题。 async
和 promises 可以将在这种情况下创建的“回调 hell ”变成一个相对干净的解决方案......它更容易阅读,更容易看到异步发生的地方以及何时需要进行编辑,您不必担心您处于哪个回调级别/正在编辑/等等。
关于javascript - 了解 node.js 异步性 - for 循环与嵌套回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24559134/
我是 PHP 新手。我一直在脚本中使用 for 循环、while 循环、foreach 循环。我想知道 哪个性能更好? 选择循环的标准是什么? 当我们在另一个循环中循环时应该使用哪个? 我一直想知道要
我在高中的编程课上,我的作业是制作一个基本的小计和顶级计算器,但我在一家餐馆工作,所以制作一个只能让你在一种食物中读到。因此,我尝试让它能够接收多种食品并将它们添加到一个价格变量中。抱歉,如果某些代码
这是我正在学习的一本教科书。 var ingredients = ["eggs", "milk", "flour", "sugar", "baking soda", "baking powder",
我正在从字符串中提取数字并将其传递给函数。我想给它加 1,然后返回字符串,同时保留前导零。我可以使用 while 循环来完成此操作,但不能使用 for 循环。 for 循环只是跳过零。 var add
编辑:我已经在程序的输出中进行了编辑。 该程序要求估计给定值 mu。用户给出一个值 mu,同时还提供了四个不等于 1 的不同数字(称为 w、x、y、z)。然后,程序尝试使用 de Jaeger 公式找
我正在编写一个算法,该算法对一个整数数组从末尾到开头执行一个大循环,其中包含一个 if 条件。第一次条件为假时,循环可以终止。 因此,对于 for 循环,如果条件为假,它会继续迭代并进行简单的变量更改
现在我已经习惯了在内存非常有限的情况下进行编程,但我没有答案的一个问题是:哪个内存效率更高;- for(;;) 或 while() ?还是它们可以平等互换?如果有的话,还要对效率问题发表评论! 最佳答
这个问题已经有答案了: How do I compare strings in Java? (23 个回答) 已关闭 8 年前。 我正在尝试创建一个小程序,我可以在其中读取该程序的单词。如果单词有 6
这个问题在这里已经有了答案: python : list index out of range error while iteratively popping elements (12 个答案) 关
我正在尝试向用户请求 4 到 10 之间的整数。如果他们回答超出该范围,它将进入循环。当用户第一次正确输入数字时,它不会中断并继续执行 else 语句。如果用户在 else 语句中正确输入数字,它将正
我尝试创建一个带有嵌套 foreach 循环的列表。第一个循环是循环一些数字,第二个循环是循环日期。我想给一个日期写一个数字。所以还有另一个功能来检查它。但结果是数字多次写入日期。 Out 是这样的:
我想要做的事情是使用循环创建一个数组,然后在另一个类中调用该数组,这不会做,也可能永远不会做。解决这个问题最好的方法是什么?我已经寻找了所有解决方案,但它们无法编译。感谢您的帮助。 import ja
我尝试创建一个带有嵌套 foreach 循环的列表。第一个循环是循环一些数字,第二个循环是循环日期。我想给一个日期写一个数字。所以还有另一个功能来检查它。但结果是数字多次写入日期。 Out 是这样的:
我正在模拟一家快餐店三个多小时。这三个小时分为 18 个间隔,每个间隔 600 秒。每个间隔都会输出有关这 600 秒内发生的情况的统计信息。 我原来的结构是这样的: int i; for (i=0;
这个问题已经有答案了: IE8 for...in enumerator (3 个回答) How do I check if an object has a specific property in J
哪个对性能更好?这可能与其他编程语言不一致,所以如果它们不同,或者如果你能用你对特定语言的知识回答我的问题,请解释。 我将使用 c++ 作为示例,但我想知道它在 java、c 或任何其他主流语言中的工
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
我是 C 编程和编写代码的新手,以确定 M 测试用例的质因数分解。如果我一次只扫描一次,该功能本身就可以工作,但是当我尝试执行 M 次时却惨遭失败。 我不知道为什么 scanf() 循环有问题。 in
这个问题已经有答案了: JavaScript by reference vs. by value [duplicate] (4 个回答) 已关闭 3 年前。 我在使用 TSlint 时遇到问题,并且理
我尝试在下面的代码中添加 foreach 或 for 循环,以便为 Charts.js 创建多个数据集。这将允许我在此折线图上创建多条线。 我有一个 PHP 对象,我可以对其进行编码以稍后填充变量,但
我是一名优秀的程序员,十分优秀!