循环进行mongoose操作为什么总是重复最后一条?见鬼!Nodejs [CLOSED]

循环进行mongoose操作为什么总是重复最后一条?见鬼!Nodejs [CLOSED]

   //我这里是想先查询数据库是否已经保护這条数据 如果没有的话 后再进行保存 防止重复
TweetDAO.prototype.save = function (objs, callback) {
var const_count = 0;
for(var obj in objs){
console.log(obj+" : “+ objs[obj].id);
var temp = objs[obj];
findOneById(objs[obj].id,function(findResult,query){
console.log(“id:”+query + " 是否有数据:”+(findResult!= null) );
if(findResult =="" || findResult ==null){
var instance = new Tweet(temp);
instance.save();
}

     });
  }

}; var findOneById = function(query,callback) { Tweet.findOne({id:query}, function(err,data){ console.log(data); callback(data,query); }); }

但是每次我发现 每次插入全部都是重复最后一条啊!


4 回复

你遇到的问题是因为异步操作的回调函数在循环结束后才被执行,导致所有的 findOneById 操作都使用了相同的 obj 值。为了解决这个问题,你可以使用 async 库来管理异步操作。

示例代码

首先,安装 async 库:

npm install async

然后修改你的代码:

const async = require('async');

TweetDAO.prototype.save = function (objs, callback) {
    const tasks = objs.map((obj) => {
        return (done) => {
            console.log(obj.id);
            findOneById(obj.id, (findResult) => {
                console.log("id:" + obj.id + " 是否有数据:" + (findResult != null));
                if (findResult == null) {
                    const instance = new Tweet(obj);
                    instance.save(done);
                } else {
                    done();
                }
            });
        };
    });

    async.series(tasks, callback);
};

var findOneById = function (query, callback) {
    Tweet.findOne({ id: query }, function (err, data) {
        console.log(data);
        callback(data);
    });
};

解释

  1. 使用 async 管理异步操作

    • async.series 方法可以确保所有任务按顺序执行,并且每个任务完成后才会开始下一个任务。
  2. 映射对象到任务数组

    • 使用 objs.map 将每个对象映射成一个任务函数,这样每个任务都有自己的上下文,不会被其他任务覆盖。
  3. 异步回调处理

    • findOneById 的回调中,如果找到结果,则直接调用 done() 结束当前任务;如果没有找到结果,则创建新的 Tweet 实例并保存,保存成功后调用 done() 结束任务。

通过这种方式,你可以确保每个对象的操作是独立的,并且按顺序执行,避免了重复最后一条数据的问题。


问题出在这里 var instance = new Tweet(temp);

你这里取的temp 永远都是objs里的最后一条记录 应为你的回调函数是最后执行的,这行循环早就执行完了,所以此时你再去取 temp的值肯定就是最后一条啦

了解一下闭包吧

这个如何改进了?麻烦告诉一下。

你的问题在于异步操作的控制流程。你使用了 findOneById 方法来查找数据,但因为这是一个异步操作,所以 for 循环不会等待 findOneById 完成后再继续执行。这导致所有的 findOneById 操作几乎同时完成,而此时循环可能已经完成了多次,因此最后保存的数据总是最新的 obj

你需要确保每个 findOneById 操作完成后才能继续下一个。可以使用 async 库来处理这种异步操作的顺序问题。下面是一个修改后的代码示例:

const async = require('async');

TweetDAO.prototype.save = function (objs, callback) {
    async.eachSeries(objs, (obj, cb) => {
        console.log(obj + " : " + objs[obj].id);
        var temp = objs[obj];
        findOneById(objs[obj].id, (findResult) => {
            console.log("id:" + temp.id + " 是否有数据:" + (findResult != null));
            if (findResult == "" || findResult == null) {
                var instance = new Tweet(temp);
                instance.save(cb); // 使用cb来处理保存完成后的回调
            } else {
                cb(); // 数据已存在,直接回调
            }
        });
    }, callback); // 所有对象处理完成后调用callback
};

var findOneById = function (query, callback) {
    Tweet.findOne({ id: query }, function (err, data) {
        console.log(data);
        callback(data ? true : false); // 简化返回值,如果找到数据返回true,否则返回false
    });
};

解释:

  1. async.eachSeries:它会按顺序依次处理数组中的每个元素,并在当前元素处理完成后才会开始处理下一个。
  2. instance.save(cb):在保存实例后调用回调函数,以确保异步操作的顺序正确。
  3. 简化返回值:在 findOneById 中,我们简化了返回值逻辑,以便于判断数据是否存在。

这样,你可以确保每个 findOneById 操作完成后才会进行下一步,从而避免所有操作都处理同一个对象的问题。

回到顶部