回调函数运行的顺序

代码如下,data是长度为3的一个数组,cmt是个object

function addComments(campground, cmt) {
    Comment.create(
        cmt,
        function(err, comment) {
            if (err) console.log(err);
            else {
                campground.comments.push(comment);
                campground.save();
                console.log("Created new comment");
        }
    });
}
    
function addCamps(seed) {
    Camp.create(seed, function(err, campground){
        if (err) console.log(err);
        else {
            console.log("Added a campground");
            addComments(campground, cmt);    
        }
    });
}


function seedDB(){
    Camp.remove({}, function(err) {
        if (err) console.log(err);
        else {
            console.log("removed campgrounds");
            Comment.remove({}, function(err) {
                if (err) console.log(err);
                else {
                    console.log("removed comments");                    
                    data.forEach(addCamps);
                }
            });
        }   
    });
}

seedDB();

请问为什么输出结果是

removed campgrounds
removed comments
Added a campground
Added a campground
Added a campground
Created new comment
Created new comment
Created new comment

而不是

removed campgrounds
removed comments
Added a campground
Created new comment
Added a campground
Created new comment
Added a campground
Created new comment

如果想让结果变成第二种情况,应该如何修改以上代码?
应该如何修改以上代码?

阅读 4.4k
2 个回答

因为回调函数的特点就是无需等待,且回调执行的时间也不确定。你这里出现这种输出顺序的原因是执行addComments时花了些时间。

如果你想要按Added a campground、Created new comment这样交替打印的话,就需要等待前一个 comment完成再执行下一个capmgroup的添加了。这就放弃了js异步的优势。现在data数据只有三个不算多,但是数据一多就会影响体验,用户会看到最后面的comment添加的比第一个迟的多。

代码的话可以用Promise:

function addComments(campground, cmt,resolve) {
  Comment.create(cmt, function(err, comment) {
    if (err) console.log(err);
    else {
      campground.comments.push(comment);
      campground.save();
      console.log("Created new comment");
      resolve();
    }
  });
}

function new_addCamps(seed) {
  return new Promise((resolve, reject) => {
    Camp.create(seed, function(err, campground) {
      if (err) reject(err);
      else {
        console.log("Added a campground");
        addComments(campground, cmt,resolve);
      }
    });
  });
}

同时不能再使用forEach而是使用for循环:

(async function (){
for(let i=0;i<data.length;i++){
await addCamps(data[i]);
}
})();

楼上正解,举个简单的例子
for (var i=0;i<3;i++){

setTimeout(function(){console.log(i)},100)

}
你觉得输出结果是什么?

答案是三个3
异步回调,普通的回调是没问题的,就是把setTimeout去掉,变成一个闭包函数。
输出是0,1,2
但是异步回调会让事件轮询的顺序产生变化。

特别详细的去百度一下Js的事件轮询机制吧

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题