Javascript和Mongoose for-loop在执行.find之前完成

我和我的伴侣需要获得每部电影的平均评分。 电影和评分是我们的MongoDB数据库的两个独立的集合。

所以,首先我们需要得到所有的电影。 之后,我们仍然需要的是在返回的电影列表的长度内循环遍历一个for循环。 在每一次迭代中,我们对当前在for循环的索引上的电影的Rating.find进行tt_number 。 在这一点上,它应该计算所有电影评分的平均值,并将结果存储在stringJSON中。 这是为每部电影完成的。

但是,我在这里包含的代码并没有这样做。 相反,for循环首先完成,然后它执行Rating.find三次,除了每次最后迭代的电影的tt_number

 var express = require('express'); var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/Notflix', {useMongoClient: true}); var Movie = require('../model/movies.js'); var Rating = require('../model/ratings.js'); var jwt = require('jsonwebtoken'); var router = express.Router(); router.get('/', function (req, res) { var jsonString = '{"The average ratings":[]}'; var obj = JSON.parse(jsonString); var moviett_number; Movie.find({}, function (err, movies) { if (err) { res.status(500); res.json({errorMessage: '500 SERVER-SIDE ERROR - No list of movies to get the average ratings of could be found.'}); } for (var i = 0; i < movies.length; i++) { //TODO: I THINK THIS FOR LOOP IS COMPLETED FULLY BEFORE FINALLY THE RATING.FIND IS DONE??????? //Go through the list of all movies, getting the ratings for each... moviett_number = movies[i].tt_number; console.log(i + " - " + moviett_number); Rating.find({'tt_number': moviett_number}, function (err, movieRatings) { //Get all the ratings for the current movie... if (err) { res.status(500); res.json({errorMessage: '500 SERVER-SIDE ERROR - No list of average ratings for this movie could be found.'}); return; } if (movieRatings > 0) { //If it has ratings, calculate the average rating. var amountOfRatings; var average = 0; for (amountOfRatings = 0; amountOfRatings < movieRatings.length; amountOfRatings++) { average += parseInt(movieRatings[amountOfRatings].rating); } average = Math.round((average / amountOfRatings) * 100) / 100; //Add the average rating for this movie to the response jsonString. obj["The average ratings"].push({ averageRatingMessage: 'Movie with tt_number = [' + moviett_number + '] has the following average rating.', averageRating: average }); } else { //Add a notice that this movie does not have any ratings and therefore no average rating either to the response jsonString. obj["The average ratings"].push({noRatingMessage: 'Movie with tt_number = [' + moviett_number + "] has no ratings yet."}); } //TODO: WATCH FOR THIS CONSOLE.LOG, IT SHOWS A WEIRD ISSUE. console.log(obj); }); } jsonString = JSON.stringify(obj); //TODO: ASYNCHRONOUS ISSUE, RESPONSE IS SET BEFORE THE CODE ABOVE IS DONE, BECAUSE THE PROGRAM CONTINUES EVEN IF THE FOR LOOP ISN'T DONE YET! console.log(jsonString); res.status(200); res.json(jsonString); }); }); 

输出:

 0 - 123 1 - 456 2 - 789 {"The average ratings":[]} { 'The average ratings': [ { noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' } ] } { 'The average ratings': [ { noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' }, { noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' } ] } { 'The average ratings': [ { noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' }, { noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' }, { noRatingMessage: 'Movie with tt_number = [789] has no ratings yet.' } ] } 

更新

这个问题不是其他问题的重复,因为这个问题涉及响应构build者和dynamic响应内容。 其他的一般是关于如何处理循环和任务要在别人之前完成。 他们接近我正在寻找的东西,但我没有find这些要么直接寻找2小时,再加上他们只是错过了我在找什么。

Model.find()方法是asynchronous的,因此您在从数据库返回之前发送结果。 一旦您提取所有数据以实际发送响应,您将需要使用callback。

 // callback when all your Rating.find() are complete function done(movies) { res.json(movies); } // get the movies Movie.find({}, function (err, movies) { // loop over the results movies.forEach(function(movie, i) { // get the Rating for this Movie Rating.find({'tt_number': movie.tt_number}, function (err, ratings) { // add the rating movie.rating = ratings.reduce(function(total, rating) { total += rating; return total; }, 0) / ratings.length; // this is the last one, call done() if (movies.length-1 === i) { done(movies); } }); } }); 

这是因为Node.js如何工作而发生的。 它首先执行for循环,它看到一个Rating.find来执行,但是由于Node.js不能同时执行2件事情(并且它已经忙于for循环),它将Rating.find添加到一个栈中稍后执行(当Node.js是免费的时候),在你的情况下发生3次,因为你有3部电影。 当for循环完成时,现在Node.js可以自由地执行Rating.find,但是现在我的值(来自for循环)已经是3了,这就是为什么它总是插入最后一个分数的原因。

为了解决这个问题(有多种方法),你可以解决IIFE(Inmediatly Invoked Function Expression)中的Rating.find。 检查如下:

 var express = require('express'); var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/Notflix', {useMongoClient: true}); var Movie = require('../model/movies.js'); var Rating = require('../model/ratings.js'); var jwt = require('jsonwebtoken'); var router = express.Router(); router.get('/', function (req, res) { var jsonString = '{"The average ratings":[]}'; var obj = JSON.parse(jsonString); var moviett_number; Movie.find({}, function (err, movies) { if (err) { res.status(500); res.json({errorMessage: '500 SERVER-SIDE ERROR - No list of movies to get the average ratings of could be found.'}); } for (var i = 0; i < movies.length; i++) { //TODO: I THINK THIS FOR LOOP IS COMPLETED FULLY BEFORE FINALLY THE RATING.FIND IS DONE??????? //Go through the list of all movies, getting the ratings for each... moviett_number = movies[i].tt_number; console.log(i + " - " + moviett_number); (function() { Rating.find({'tt_number': moviett_number}, function (err, movieRatings) { //Get all the ratings for the current movie... if (err) { res.status(500); res.json({errorMessage: '500 SERVER-SIDE ERROR - No list of average ratings for this movie could be found.'}); return; } if (movieRatings > 0) { //If it has ratings, calculate the average rating. var amountOfRatings; var average = 0; for (amountOfRatings = 0; amountOfRatings < movieRatings.length; amountOfRatings++) { average += parseInt(movieRatings[amountOfRatings].rating); } average = Math.round((average / amountOfRatings) * 100) / 100; //Add the average rating for this movie to the response jsonString. obj["The average ratings"].push({ averageRatingMessage: 'Movie with tt_number = [' + moviett_number + '] has the following average rating.', averageRating: average }); } else { //Add a notice that this movie does not have any ratings and therefore no average rating either to the response jsonString. obj["The average ratings"].push({noRatingMessage: 'Movie with tt_number = [' + moviett_number + "] has no ratings yet."}); } //TODO: WATCH FOR THIS CONSOLE.LOG, IT SHOWS A WEIRD ISSUE. console.log(obj); }); })(); } jsonString = JSON.stringify(obj); //TODO: ASYNCHRONOUS ISSUE, RESPONSE IS SET BEFORE THE CODE ABOVE IS DONE, BECAUSE THE PROGRAM CONTINUES EVEN IF THE FOR LOOP ISN'T DONE YET! console.log(jsonString); res.status(200); res.json(jsonString); });