Почему одна функция не ожидает разрешения Promise.all () в другой функции? - PullRequest
0 голосов
/ 25 мая 2020

У меня есть простая форма, которая позволяет загружать файлы.

<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() и как мне это исправить, чтобы он ожидал завершения загрузки всех файлов sh перед отправкой формы?

Ответы [ 3 ]

1 голос
/ 25 мая 2020

На вашем @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].isUploading равно true для всех файлов. Итак, ваш код для этого пути по существу выглядит следующим образом:

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 разрешается «немедленно», не дожидаясь загрузки файлов в fini sh. Successfully uploaded all files регистрируется дважды (если вы не перезагружаете страницу после отправки формы), и если теперь журнал происходит между ними, вы увидите его только один раз со значком перед ним, показывающим 2.

Что вы можете сделать, так это сохранить обещание, возвращаемое аксиомами, в объекте, в котором вы храните 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 чехол.

0 голосов
/ 25 мая 2020

Я думаю, вам нужно вернуть обещание из файла 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)
  }
0 голосов
/ 25 мая 2020

хотя это не отвечает на ваш конкретный c вопрос, вы можете отключить кнопку для отправки формы, пока идет загрузка, а также проверить, идет ли загрузка, перед отправкой формы.

...