Node.js 异步性问题:为什么函数的第二部分在循环事件完成之前运行?

Node.js 异步性问题:为什么函数的第二部分在循环事件完成之前运行?,node.js,mongodb,express,Node.js,Mongodb,Express,我在Express服务器上有一个更新用户配置文件的路由。在我解析完要更新的数据之前,用户配置文件已经更新。怎么会这样 我想更新两个常量:newProfilePicture和newOtherPictures。它们被正确地更新了,但是在用户被更新之后,所以它是无用的。如何解决这个异步性问题 以下是函数: router.post(“/upload images”,upload.array(“image”),异步(req,res)=>{ const{userId}=req.body; 试一试{ 如果(

我在Express服务器上有一个更新用户配置文件的路由。在我解析完要更新的数据之前,用户配置文件已经更新。怎么会这样

我想更新两个常量:newProfilePicture和newOtherPictures。它们被正确地更新了,但是在用户被更新之后,所以它是无用的。如何解决这个异步性问题

以下是函数:

router.post(“/upload images”,upload.array(“image”),异步(req,res)=>{
const{userId}=req.body;
试一试{
如果(请求文件){
让newProfilePicture=null;
让newOtherPictures=[];
req.files.forEach({path,originalname})=>{
cloudinary.uploader.upload(
路径
{
资源类型:“图像”,
public_id:`myapp/users/${userId}/${originalname}`,
作物:“规模”,
品质:“自动”,
},
(err,res)=>{
如果(错误){
返回fs.unlinkSync(“./”+路径);
}
fs.取消链接同步(“./”+路径);
如果(原始名称==“主”){
返回(newProfilePicture=res.secure\u url);
}
返回newOtherPictures.push({
id:原始名称,
url:res.secure\u url,
});
}
);
});
//此部分在req.files.forEach完成之前完成
const user=await user.findById(userId);
const{otherPictures,profilePicture}=updatePictures(
newProfilePicture,
新的其他图片,
使用者
);
User.findByIdAndUpdate(
用户ID,
{profilePicture,otherPictures},
{新:正确}
);
res.send(“上传图像成功”);
}
}捕捉(错误){
console.log(“err”,err);
返回res.status(500)。发送(“上传图像失败”);
}

});之所以发生这种情况,是因为
cloudinary.uploader.upload()异步运行。因为您提到它没有promise接口,所以可以使用NodeJS的
util.promise
函数将回调转换为promise,因为它是错误第一次回调

const { promisify } = require("util");
const fs = require("fs");
const cloudinaryUpload = promisify(cloudinary.uploader.upload.bind(cloudinary.uploader))

router.post("/upload-images", upload.array("image"), async (req, res) => {
  try {
    if (!req.files) {
      return res.send("no images in the request body");
    }
    let newProfilePicture = null;
    let newOtherPictures = [];
    for (const { path, originalName } of req.files) {
      try {
        const response = await cloudinaryUpload(path, {
          resource_type: "image",
          public_id: `myapp/users/${userId}/${originalName}`,
          crop: "scale",
          quality: "auto",
        });
        await fs.promises.unlink("./" + path);
        if (originalname === "main") {
          newProfilePicture = response.secure_url;
          continue;
        }
        newOtherPictures.push({
          id: originalName,
          url: response.secure_url,
        });
      } catch (error) {
        //do what you want when if there is an error
        //throw error if you want
        await fs.promises.unlink("./" + path);
      }
    }
    const user = await User.findById(userId);
    const { otherPictures, profilePicture } = updatePictures(
      newProfilePicture,
      newOtherPictures,
      user
    );
    //use User.updateOne() as you don't need the doc back
    await User.findByIdAndUpdate(
      userId,
      { profilePicture, otherPictures },
      { new: true }
    );
    return res.send("upload images success");
  } catch (error) {
    console.log("err", err);
    return res.status(500).send("upload images failed");
  }
});

这是因为
cloudinary.uploader.upload()
异步运行。因为您提到它没有promise接口,所以可以使用NodeJS的
util.promise
函数将回调转换为promise,因为它是错误第一次回调

const { promisify } = require("util");
const fs = require("fs");
const cloudinaryUpload = promisify(cloudinary.uploader.upload.bind(cloudinary.uploader))

router.post("/upload-images", upload.array("image"), async (req, res) => {
  try {
    if (!req.files) {
      return res.send("no images in the request body");
    }
    let newProfilePicture = null;
    let newOtherPictures = [];
    for (const { path, originalName } of req.files) {
      try {
        const response = await cloudinaryUpload(path, {
          resource_type: "image",
          public_id: `myapp/users/${userId}/${originalName}`,
          crop: "scale",
          quality: "auto",
        });
        await fs.promises.unlink("./" + path);
        if (originalname === "main") {
          newProfilePicture = response.secure_url;
          continue;
        }
        newOtherPictures.push({
          id: originalName,
          url: response.secure_url,
        });
      } catch (error) {
        //do what you want when if there is an error
        //throw error if you want
        await fs.promises.unlink("./" + path);
      }
    }
    const user = await User.findById(userId);
    const { otherPictures, profilePicture } = updatePictures(
      newProfilePicture,
      newOtherPictures,
      user
    );
    //use User.updateOne() as you don't need the doc back
    await User.findByIdAndUpdate(
      userId,
      { profilePicture, otherPictures },
      { new: true }
    );
    return res.send("upload images success");
  } catch (error) {
    console.log("err", err);
    return res.status(500).send("upload images failed");
  }
});

因为
cloudinary.uploader.upload(()
是非阻塞和异步的。您的
.forEach()
循环运行到完成,启动所有
cloudinary.uploader.upload(()
调用running,然后运行函数的第二部分,稍后运行所有
cloudinary.uploader.upload()
调用完成。
cloudinary.uploader.upload(
有一个promise接口可以用来代替普通的回调接口吗?我不这么认为,没有。
.forEach()
循环中的两个
return
语句的意义是什么。它们应该做什么?因为
.forEach()
不关注回调返回的值,它们当前没有做任何有用的事情。但是,我想知道它们应该完成什么。这里有类似的问题/答案:因为
cloudinary.uploader.upload(()
是非阻塞和异步的。您的
.forEach()
循环运行到完成,启动所有
cloudinary.uploader.upload(()
调用running,然后运行函数的第二部分,然后,稍后所有
cloudinary.uploader.upload(()
调用finish.Does
cloudinary.uploader.upload()
有一个promise接口可以用来代替普通的回调接口吗?我不这么认为,没有。在
.forEach()
循环中的两个
return
语句有什么意义呢?既然
.forEach()
不关注回调返回的值,它们目前没有做任何有用的事情。但是,我想知道它们应该完成什么。类似的问题/答案如下:您遗漏了函数中更新配置文件的第二部分,这是在origina中错误时间执行的部分l问题。>在原始问题中,哪个部分在错误的时间执行?是的。上面的代码将等待第一部分完成执行,然后再开始执行第二部分。因此,为什么不完成答案并将该代码包含在路由处理程序中?您现在的答案是说明性的,但当您uld很好地通过在路由处理程序中包含其余代码来完成。同步功能块和回调将导致@DoneDeal0。您可以添加
continue
以不再继续(我在回答中添加了)您遗漏了更新配置文件的函数的第二部分,即原始问题中在错误时间执行的部分。>原始问题中在错误时间执行的部分是。上面的代码将等待第一部分完成执行,然后再开始第二部分。那么,为什么不呢您完成了答案并将该代码包含在路由处理程序中?您现在的答案是说明性的,但如果您可以通过将其余代码包含在路由处理程序中来完成答案,则答案是不完整的。同步函数块和回调将导致@DoneDeal0您可以添加
继续
,这样就不必进一步了(我在答覆中补充)