为什么一个函数不等待Promise.all()在另一个函数中解析?

时间:2020-05-25 11:18:40

标签: javascript node.js vue.js promise async-await

我有一个简单的表格,可以进行文件上传。

<form @submit.prevent="sendForm" enctype="multipart/form-data">
  <input multiple ref="PostFiles" name="PostFiles" type="file" @change="selectFile('add')">
  <!-- other fields here -->
  <button type="submit" name="Send" value="Send"></button>
</form>
  1. selectFile()方法调用uploadFile()方法,以使用axios.post()请求将用户选择的文件发送到服务器。
  2. 如果用户决定在上传仍处于静止状态时提交表单 发生,那么await需要selectFile()才能完成 提交之前。

这是selectFile()的工作,可以正常工作。等待uploadFile()解决,然后在所有上传完成后在控制台中收到成功消息:

async selectFile(action) {
  if (action === 'add') {
    // It adds the files to the array this.Form.PostFiles
    let PostFiles = this.$refs.PostFiles.files;
    if (this.Form.PostFiles == null || this.Form.PostFiles.length === 0) {
      this.Form.PostFiles = [...this.Form.PostFiles, ...PostFiles].map(file => { //returns a new array with the file object and metainfo object
        return [
          file, {
            originalFilename: file.name,
            uploadedFilename: "",
            size: file.size,
            type: file.type,
            isUploading: false,
            isUploaded: false,
            uploadProgress: 0,
            previewImgSrc: URL.createObjectURL(file)

          }
        ]
      });
    } else {
      PostFiles = Array.from(PostFiles).map(file => {
        return [
          file, {
            originalFilename: file.name,
            uploadedFilename: "",
            size: file.size,
            type: file.type,
            isUploading: false,
            isUploaded: false,
            uploadProgress: 0,
            previewImgSrc: URL.createObjectURL(file)
          }
        ]
      });
      this.Form.PostFiles.push(...PostFiles);

    }
  }

  try {
    return Promise.all(
        this.Form.PostFiles.map(async (file, i) => {
          const File = file[0];
          if (this.Form.PostFiles[i][1].isUploaded == false &&
            this.Form.PostFiles[i][1].isUploading == false) {
            await this.uploadFile({File: File, FileIndex: i});
          }
        })
      )
      .then(result => {
        console.log("Successfully uploaded all files")
      }).catch(err => {
        console.log(err)
      })
  } catch (err) {
    console.log(err)
  }
}

uploadFile()方法使用axios上传文件,如下所示:

async uploadFile({ File, FileIndex }) {
  const FormFile = new FormData();
  FormFile.append("UploadFile", File);

 this.Form.PostFiles[FileIndex][1].isUploading = true;

  return this.$axios.post('/api/post', FormFile)
     .then(response => {
       this.Form.PostFiles[FileIndex][1].isUploading = false;
       this.Form.PostFiles[FileIndex][1].isUploaded = true;
     }).catch(err => {
       console.log(err)
     })

因此,这里是发生问题的地方。如果在上传仍在进行时用户点击了表单的“提交”按钮,则sendForm()方法不会等待selectFile()来解决所有承诺,然后再提交文件:

async sendForm() {
const FormBody = new FormData();
FormBody.append("PostTitle", this.Form.PostTitle);
FormBody.append("PostDescription", this.Form.PostDescription);
  try {
    await this.selectFile();
    this.$axios.post('/api/post', FormBody).then(response => {
      console.log("Form submitted");
    }).catch(err => {
      console.log(err.response.data.error)
    })

  } catch (error) {
    console.log(error);
  }
}

我在控制台中收到一条消息,提示“表单已提交”,尽管可能仍在进行3次上传。似乎在第一次上传完成后立即打印“表单已提交”。为什么不等待selectFile()?如何解决此问题,以便它在提交表单之前等待所有文件完成上传?

3 个答案:

答案 0 :(得分:1)

@change="selectFile('add')"上,将文件添加到this.$refs.PostFiles并启动上载。现在您正在this.Form.PostFiles[FileIndex][1].isUploading = true;进行上传。 this.Form.PostFiles.map(async (file, i) => { … }保留axios返回的承诺。因此,一旦所有文件上传完毕,您就可以正确看到Successfully uploaded all files

对于sendForm,您再次调用selectFile,这部分代码将再次执行:

return Promise.all(
    this.Form.PostFiles.map(async (file, i) => {
      const File = file[0];
      if (this.Form.PostFiles[i][1].isUploaded == false &&
        this.Form.PostFiles[i][1].isUploading == false) {
        await this.uploadFile({File: File, FileIndex: i});
      }
    })
  )
  .then(result => {
    console.log("Successfully uploaded all files")
  }).catch(err => {
    console.log(err)
  })

但是现在所有文件的this.Form.PostFiles[i][1].isUploadingtrue。因此,您的代码基本上就是这样的:

return Promise.all(
    this.Form.PostFiles.map(async (file, i) => {
      const File = file[0];
    })
  )
  .then(result => {
    console.log("Successfully uploaded all files")
  }).catch(err => {
    console.log(err)
  })

因此Promise.all可以“立即”解决,而不是等待上传文件完成。 Successfully uploaded all files被记录两次(如果您在提交表单后不重新加载页面),并且如果现在在两次记录之间进行记录,则您只会看到一次,并且前面有一个显示2的标志。

您可以做的是保存公理返回的Promise,并将其保存在存储isUploading的对象中,并在代码的else分支中使用它,以防上载到进度或完成:

return Promise.all(
    this.Form.PostFiles.map(async (file, i) => {
      const File = file[0];
      if (this.Form.PostFiles[i][1].isUploaded == false &&
        this.Form.PostFiles[i][1].isUploading == false) {
        this.Form.PostFiles[i][1].uploadPromise = this.uploadFile({File: File, FileIndex: i});
        await this.Form.PostFiles[i][1].uploadPromise;
      } else {
        await this.Form.PostFiles[i][1].uploadPromise;
      }
    })
  )
  .then(result => {
    console.log("Successfully uploaded all files")
  }).catch(err => {
    console.log(err)
  })

虽然这可以解决问题,但将它们组合成一个函数仍然不是一种好方法,因为名称selectFile对于在submitForm情况下的行为具有极大的误导性情况。

答案 1 :(得分:-1)

我认为您必须将promise从uploadFile返回到映射数组:

return Promise.all(
        this.Form.PostFiles.map(async (file, i) => {
          const File = file[0];
          return await this.uploadFile({
            File: File
          });
        })
      )
      .then(result => {
        console.log("Successfully uploaded all files")
      }).catch(err => {
        console.log(err)
      })
  } catch (err) {
    console.log(err)
  }

答案 2 :(得分:-3)

尽管这不能回答您的特定问题,但是您可以在上传正在进行时禁用表单提交按钮,还可以在提交表单之前检查是否正在进行任何上传。