node js async & await 函数 - 强制函数完成?

问题描述

我正在尝试使用组件(summernote)以角度形式上传图像。

我配置了 uploadImagePath 端点以将图像提交到我的 nodejs 后端,但在我的代码中,outputFileName 字段返回空。

如何确保功能完成?

async uploadFile(req) {

    var multiparty = require('multiparty');
    var form = new multiparty.Form();
    
    let outputFileName = '';

    try {

        form.parse(req,function(err,fields,files) {  
            var imgArray = files.image;

            for (var i = 0; i < imgArray.length; i++) {
                
                var singleImg = imgArray[i];
                outputFileName = singleImg.originalFilename;
                
                fs.readFile(singleImg.path,data) {
                    fs.writeFile('./public/images/'+outputFileName,data,function(err) {
                        if (err) console.log('ERRRRRR!! :'+err);
                        console.log('Fitxer: ' + outputFileName);
                    })
                })       
            }
        });
    } 
    catch (ex) {
        throw ex;
    }

    return { error: false,outputFileName: outputFileName,msg: "File uploaded!" };
}

解决方法

您不应在 async 函数中直接调用接受回调的函数。这就是为什么您的 try-catch 捕获任何错误的机会较小的原因。相反,返回一个 Promise 来处理该错误。

function uploadFile(req) {
  return new Promise((resolve,reject) => {
    const multiparty = require('multiparty')
    const form = new multiparty.Form()
    let outputFileName = ''
    form.parse(req,function (err,fields,files) {
      if (err) {
        reject(err)
        return
      }
      const imgArray = files.image

      // You should not call function that accepts a callback in for loop
      // Lets assumed imageArray has only one element.
      // for (let i = 0; i < imgArray.length; i++) {
        const singleImg = imgArray[0 /* i */]
        outputFileName = singleImg.originalFilename
        fs.readFile(singleImg.path,data) {
          if (err) {
            reject(err)
            return
          }
          fs.writeFile('./public/images/' + outputFileName,data,function (err) {
            if (err) {
              reject(err)
              return
            }
            console.log('Fitxer: ' + outputFileName)
            resolve({
              error: false,outputFileName: outputFileName,msg: "File uploaded!"
            })
          })
        })
      // }
    });
  })
}

但是如您所见,上面的代码很乱,for 循环不起作用。为了解决这个问题,我们应该使 uploadFile 异步,不能使用回调。

如果可能,async 函数应该调用另一个返回 Promise 的函数,这样 await 关键字有助于简化代码并处理抛出的错误。要实现这样的用法,请编写一个新函数来使 form.parse 异步,并将 fs 替换为支持返回 Promise 的东西,例如 fs.Promise APIfs-extra package .

const fs = require('fs-extra')
const multiparty = require('multiparty')

function parseForm(req) {
  return new Promise((resolve,reject) => {
    const form = new multiparty.Form()
    form.parse(req,files) {
      if (err) reject(err)
      else resolve(files)
    })
  })
}

async function uploadFile(req) {
  let outputFileName = ''
  const files = await parseForm(req)
  const imgArray = files.image

  // Now we can use for loop
  for (let i = 0; i < imgArray.length; i++) {
    const singleImg = imgArray[i]
    outputFileName = singleImg.originalFilename
    const data = await fs.readFile(singleImg.path)
    await fs.writeFile('./public/images/' + outputFileName,data)
    console.log('Fitxer: ' + outputFileName)
  }
  return {
    error: false,msg: "File uploaded!"
  }
}

您的代码仍然存在一些问题。 outputFileName 被多次分配,这使您的目的难以理解。因此,我无法按照您的预期重写您的代码。

顺便说一句,应该考虑更多条件。这个我就不多说了。

  • 建议使用 not use var keyword 声明一个局部变量。改用 constlet
  • 避免在 for 循环中使用 await 关键字,除非您打算一个一个地迭代元素。
,

最简单的方法是创建一个辅助函数,让 nodestyle fn 返回一个 promise,然后使用它

function parseMultiparty(req) {
  return new Promise((resolve,reject) => {
    var form = new multiparty.Form();
    form.parse(req,function(err,files) { 
      if(err) reject(err) else resolve([fields,files])
    })

  })
}
async uploadFile(req) {

    var multiparty = require('multiparty');
    var form = new multiparty.Form();
    
    let outputFileName = '';

    try {
        const [fields,files] = await parseMultiparty(req)
          var imgArray = files.image;

          for (var i = 0; i < imgArray.length; i++) {
              
              var singleImg = imgArray[i];
              outputFileName = singleImg.originalFilename;
              
              const data = await fs.promises.readFile(singleImg.path)
              console.log('Fitxer: ' + outputFileName);
              try {
                await fs.promises.writeFile('./public/images/'+outputFileName,data)
              } catch(err) { console.log('ERRRRRR!! :'+err);}
          }
    } 
    catch (ex) {
        throw ex;
    }

    return { error: false,msg: "File uploaded!" };
}