Nodejs与while循环异步

时间:2015-10-28 19:38:48

标签: javascript node.js asynchronous synchronous

所以我有这段代码:

Rating.find({user: b}, function(err,rating) {
            var covariance=0;
            var standardU=0;
            var standardV=0;

            while (rating.length>0){
                console.log("the avarage rating u is:" + avarageRatingU)
                console.log("the avarage rating v is:" + avarageRatingV)
                currentMovie = rating.pop();
                var u=currentMovie.value-avarageRatingU;
                standardU = standardU + Math.pow(u,2);
                var v=0;
                Rating.find({movieid:currentMovie.movieid, user:a}, function(err,ratings) {
                    if (err) throw err;
                    if(ratings.length>0){
                        v=ratings.pop().value-avarageRatingV;
                        standardV=standardV+Math.pow(v,2);
                        covariance =covariance+u*v;
                        console.log(covariance);

                    }
                })
            }
            console.log(covariance)
            callback(null,covariance);
            //sim = covariance/(Math.sqrt(standardU)*Math.sqrt(standardV));
        })

问题是当我打印协方差时,它会打印0,因为打印在计算之前发生。我想过使用async.series,但是,我不能在while循环中使用回调函数。

任何提示将不胜感激。

Ty:)

8 个答案:

答案 0 :(得分:2)

难道你不能像这样在循环内调用你的回调吗?不确定这是否对您有所帮助。

while (rating.length>0){
  console.log("the avarage rating u is:" + avarageRatingU)
  console.log("the avarage rating v is:" + avarageRatingV)
  currentMovie = rating.pop();
  var u=currentMovie.value-avarageRatingU;
  standardU = standardU + Math.pow(u,2);
  var v=0;
  Rating.find({movieid:currentMovie.movieid, user:a}, function(err,ratings) {
    if (err) throw err;
    if(ratings.length>0){
      v=ratings.pop().value-avarageRatingV;
      standardV=standardV+Math.pow(v,2);
      covariance =covariance+u*v;
      console.log(covariance);
    }
    if(rating.length == 0){
      console.log(covariance);
      callback(null, covariance);
    }
  })
}

答案 1 :(得分:1)

使用promises或async库将是这种情况下的方式,而不是传统的while循环。由于您已熟悉异步,因此请使用async.map(https://github.com/caolan/async#map)提前获取所需的所有数据。

Rating.find({user: b}, function(err,rating) {
        var covariance=0;
        var standardU=0;
        var standardV=0;

        aync.map(rating, function(currentMovie, cb) {
            Rating.find({ movieid:currentMovie.movieid, user:a }, function(err,ratings) {
                cb(err, ratings);
            });
        }, function(err, results) {
            if (!err) {
                // compute covariance with all movie data
            }
        });
    })

答案 2 :(得分:0)

这个怎么样:

Rating.find({user: b}, function(err,rating) {
        var covariance=0;
        var standardU=0;
        var standardV=0;

        var c = rating.length; // loop counter
        var r = 0; // received counter
        function aux_callback (covariance) {
            r++;
            if (r==c)
                callback(null,covariance);
        }

        while (rating.length>0){
            console.log("the avarage rating u is:" + avarageRatingU)
            console.log("the avarage rating v is:" + avarageRatingV)
            currentMovie = rating.pop();
            var u=currentMovie.value-avarageRatingU;
            standardU = standardU + Math.pow(u,2);
            var v=0;
            Rating.find({movieid:currentMovie.movieid, user:a}, function(err,ratings) {
                if (err) throw err;
                if(ratings.length>0){
                    v=ratings.pop().value-avarageRatingV;
                    standardV=standardV+Math.pow(v,2);
                    covariance =covariance+u*v;
                    console.log(covariance);
                    aux_callback(covariance);
                }
            })
        }
        //sim = covariance/(Math.sqrt(standardU)*Math.sqrt(standardV));
    })

您需要从循环内的lambda函数触发回调,以确保在运行该代码之前不会发生回调。鉴于您似乎需要完成所有循环,我创建了两个计数器c来跟踪lambda运行的次数,并r计算你调用aux_callback的次数。此代码应等到您在调用callback

之前计算所有协方差

答案 3 :(得分:0)

这是我今晚的第二个承诺解决方案:)。所以,让我试试:

    //supposing you are using node js
    var Q = require('q'); //https://github.com/dscape/nano
    Rating.find({user: b}, function(err,rating) {
                var covariance=0;
                var standardU=0;
                var standardV=0;
                var promises = [];
                while (rating.length>0){
                    console.log("the avarage rating u is:" + avarageRatingU)
                    console.log("the avarage rating v is:" + avarageRatingV)
                    currentMovie = rating.pop();
                    var u=currentMovie.value-avarageRatingU;
                    standardU = standardU + Math.pow(u,2);
                    var v=0;
                    var def = Q.defer();
                    promises.push(def);
                    Rating.find({movieid:currentMovie.movieid, user:a}, function(err,ratings) {
                        if (err) {
                            def.reject();
                            throw err;
                        }
                        if(ratings.length>0){
                            v=ratings.pop().value-avarageRatingV;
                            standardV=standardV+Math.pow(v,2);
                            covariance =covariance+u*v;
                            def.resolve();
                            //console.log(covariance);
                        }

                    });

                }
                Q.allSettled(promises, function() {
                    console.log(covariance);    
                });          
                callback(null,covariance);
                //sim = covariance/(Math.sqrt(standardU)*Math.sqrt(standardV));
            });

答案 4 :(得分:0)

尝试使用Promise!现在Node.js 4.x已经出来了,所有的JavaScript ES6(和Promises)都附带了它。您可以查看Mozilla Foundation文档中的promises here

如果您以前从未使用过承诺,那么它真正做的就是允许您在值可用之前返回一个值。之后,承诺能够以实际价值成为已解决被拒绝

在这种情况下,您将要在new Promise( function(resolve, reject) { ... } )内封装内部的Rating.find()调用,并在您的promise上附加then()子句,该子句打印出协方差的值。当您从内部Rating.find()函数中检索实际协方差时,只需使用 resolve 参数(这是一个采用单个值的函数)并传递协方差,以便将其传递给函数在你的Promise then()子句中,它将打印协方差。

如果有任何混淆,请给我发表评论!

答案 5 :(得分:0)

你使用哪个 orm ?,我认为更好的方法是按“movieid”对结果进行分组,然后计算每个组的协方差,而不对每个“评级”进行查询结果

Rating.find({user: b}, function(err, rating){
    var covariance=0;
    var standardU=0;
    var standardV=0;
    var ratingsByMovieId = groupByMovieId(rating);
    while(rating.length > 0){
        console.log("the avarage rating u is:" + avarageRatingU)
        console.log("the avarage rating v is:" + avarageRatingV)
        currentMovie = rating.pop();
        var u=currentMovie.value-avarageRatingU;
        standardU = standardU + Math.pow(u,2);
        var v=0;
        ratingsByMovieId[currentMovie.movieid].forEach(function(ratings){
            // this code isn't asynchronous :)
            // because the code isn't asynchronous you don't need a callback.
        });
    }
});

function groupByMovieId(ratings) {
    var groups = {};
    ratings.forEach(function(rating){
        var movieId = rating.movieid;
        groups[movieId] = groups[movieId] || [];
        groups[movieId].push(rating);
    });
    return groups;
}

我没有测试此代码jeje:)

答案 6 :(得分:0)

假设评级(或评级)是一个数组,你需要迭代并在每个项目上调用异步函数(例如Rating.find),async.eachSeries可能最有意义。

async.eachSeries(ratings, function iterator(rating, callback) {
  // invoke async function here for each rating item
  // and perform calculations
}, function done() {
  // all async functions have completed, print your result here
});

此处有更多文档:https://github.com/caolan/async

答案 7 :(得分:0)

这是在循环中调用异步函数的标准问题。

让我们先看一个更简单的例子。假设我想要一个在for循环中打印i值的函数,我希望它以异步方式运行:

for (i = 0; i < 5; i++) { 
    setTimeout(function() { console.log(i); }, 1);
}

现在实际只会打印5次,5次。这是因为异步函数setTimeout在循环结束后返回,并且i在执行时的值为5.如果我们希望它打印出0 - 4,那么我们需要做以下事情:

for (i = 0; i < 5; i++) { 
    (function(i) {
        setTimeout(function() { console.log(i); }, 1);
    })(i);
}

注意匿名函数如何将i作为参数。这创造了一个“拯救”的关闭。调用时i的值。

现在回到你的问题:

covariance = covariance + u * v;

这发生在您的console.log(covariance)语句之前,这意味着您的打印语句在计算后实际 。这意味着此时covariance实际上等于0。

您的covariance变量已初始化为0. uv也是如此。 0 + 0 * 0 = 0.好的,很好:所以如果uv未正确设置,至少数学会检出。

让我们回到你的循环:

while (rating.length>0){
    currentMovie = rating.pop();
    var u=currentMovie.value-avarageRatingU;
    var v=0;
    Rating.find({movieid:currentMovie.movieid, user:a}, function(err,ratings) {
        if (err) throw err;
        if(ratings.length>0){
            v=ratings.pop().value-avarageRatingV;
            standardV=standardV+Math.pow(v,2);
            covariance =covariance+u*v;
...

在这里,我们可以看到您的异步Rating.find回调正在提取u的最后一个已知值,即其在while循环的 end 处的值。在v回调之外定义Rating.find的确没有任何理由。

如果你想为每个循环设置u的值,请尝试将其包装在一个匿名的自执行函数中以保存&#34;价值,如下:

Rating.find({user: b}, function(err,rating) {
    var covariance = 0;
    var standardU = 0;
    var standardV = 0;
    var u = 0;

    while (rating.length > 0){
        console.log("the avarage rating u is:" + avarageRatingU)
        console.log("the avarage rating v is:" + avarageRatingV)
        currentMovie = rating.pop();
        u = currentMovie.value - avarageRatingU;
        standardU = standardU + Math.pow(u, 2);
        (function(u) {
            Rating.find({ movieid: currentMovie.movieid, user: a }, function(err, ratings) {
                if (err) throw err;
                if (ratings.length > 0) {
                    var v = ratings.pop().value - avarageRatingV;
                    standardV = standardV + Math.pow(v,2);
                    covariance = covariance + u * v;
                    console.log(covariance);

                }
            });
        })(u);
    }
    console.log(covariance)
    callback(null,covariance);
    //sim = covariance/(Math.sqrt(standardU)*Math.sqrt(standardV));
});

我还在你的循环之外移动了u的声明(在里面声明它们是不好的做法,因为你每次都要重新实例化变量)。我在v回调中移动了Rating.find 的声明,因为它甚至没有在那里使用。