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

Posted

技术标签:

【中文标题】node js async & await 函数 - 强制函数完成?【英文标题】:node js async & await functions - force the function to complete? 【发布时间】:2021-05-22 07:17:26 【问题描述】:

我正在尝试使用组件(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 , function(err,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!" ;

【问题讨论】:

请更新您的问题标题以更详细地反映问题 为什么将uploadFile 设置为async 函数? 不管你有没有,都是一样的。这里的问题是返回发生在 form.parse 完成之前 【参考方案1】:

您不应直接调用在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, function (err, 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 API 或 fs-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, function (err, fields, 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,
    outputFileName: outputFileName,
    msg: "File uploaded!"
  

不过,您的代码仍然存在一些问题。 outputFileName 被多次分配,这使您的目的难以理解。出于这个原因,我无法重写您的代码,使其符合您的预期。

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

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

【讨论】:

【参考方案2】:

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

function parseMultiparty(req) 
  return new Promise((resolve, reject) => 
    var form = new multiparty.Form();
    form.parse(req, function(err, fields, 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, outputFileName: outputFileName, msg: "File uploaded!" ;

【讨论】:

以上是关于node js async & await 函数 - 强制函数完成?的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 7 的 async await 终于来了,不过怎么觉得没啥用

Node.js - 使用 'async' 和 'await' 和 sequelize ORM

Node.js 最佳实践异常处理——在 Async/Await 之后

Node.js 使用 async/await 读取文件

Node.js 使用 async/await 和 mysql

async&await的前世今生