【问题标题】:Problem with async/await with FileReader in JavaScriptJavaScript 中 FileReader 的异步/等待问题
【发布时间】:2021-05-11 10:05:41
【问题描述】:

我在一个 Vue.js 项目中使用 FileReader,但这段代码有问题:

async uploadDocuments(files) {
    for (let file of files) {
        let fileName = file.name;
        let fileContent;
        let reader = new FileReader();
        reader.onload = async () => {
            fileContent = reader.result;
            await this.submitFile(fileContent, fileName, fileType)
                .then(/* THEN block */)
                .catch(/* CATCH block */);
        };
        reader.onerror = (error) => { console.warn(error); };
        reader.readAsDataURL(file);
    }
    console.log("COMPLETED");
}

async submitFile(fileContent, fileName, fileType) {
    // this function should handle the file upload and currently contains a timeout
    await new Promise((resolve) => setTimeout(resolve, 3000));
}

这是所需的执行顺序(以两个文件为例):

  1. (等待 3 秒)
  2. THEN 块(文件 1)
  3. (等待 3 秒)
  4. THEN 块(文件 2)
  5. 已完成

但这是实际的执行顺序:

  1. 已完成
  2. (等待 3 秒)
  3. 然后阻止
  4. 然后阻止

超时后“THEN块”正确执行,但for循环中的代码继续执行,无需等待onload函数执行。

如何使阅读器异步?我尝试了很多解决方案(例如将for 循环包装在一个promise 中,并将resolve() 函数放入.then()),但它们都不起作用。

【问题讨论】:

  • 为什么要将文件内容读入内存并以base64格式上传?那是最糟糕的。 ~3 更多带宽不必要的处理,没有足够的 RAM 来处理大文件。只需将文件附加到FormData 并将其上传为application/multipart-formdata

标签: javascript async-await filereader


【解决方案1】:

我建议“承诺”阅读器,然后使用Promise.all,直到所有文件都上传完毕。

uploadDocuments = async (event, files) => {
  const filePromises = files.map((file) => {
    // Return a promise per file
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = async () => {
        try {
          const response = await this.submitFile(
            reader.result,
            file.name,
            fileType
          );
          // Resolve the promise with the response value
          resolve(response);
        } catch (err) {
          reject(err);
        }
      };
      reader.onerror = (error) => {
        reject(error);
      };
      reader.readAsDataURL(file);
    });
  });

  // Wait for all promises to be resolved
  const fileInfos = await Promise.all(filePromises);

  console.log('COMPLETED');

  // Profit
  return fileInfos;
};

【讨论】:

  • 谢谢,这行得通!我现在还有一个问题。我用实际的上传代码axios.post(...)替换了超时,并且我需要执行/* CATCH block */以防帖子失败。我尝试使用axios.post(...).then(...).catch((err) => Promise.reject(err)),但我认为这是错误的,它不适用于您的Promise.all()。你有什么建议吗?谢谢!
  • 很好,请考虑标记为已解决。您可以将 const response = await this.submitFile(...) 包装在 try/catch 块中,然后在 catch 中调用 reject。
【解决方案2】:

试试这个。

在reader.onload之前添加await,它将一直保持到那时或catch块执行成功

async uploadDocuments(event) {
    for (let file of files) {
        let fileName = file.name;
        let fileContent;
        let reader = new FileReader();
        await reader.onload = async () => {
            fileContent = reader.result;
            await this.submitFile(fileContent, fileName, fileType)
                .then(/* THEN block */)
                .catch(/* CATCH block */);
        };
        reader.onerror = (error) => { console.warn(error); };
        reader.readAsDataURL(file);
    }
    console.log("COMPLETED");
}

async submitFile(fileContent, fileName, fileType) {
    // this function should handle the file upload and currently contains a timeout
    await new Promise((resolve) => setTimeout(resolve, 3000));
}```

【讨论】:

  • reader.onload 是事件处理程序,尝试添加 await 没有意义
猜你喜欢
  • 2022-10-25
  • 1970-01-01
  • 2021-10-11
  • 1970-01-01
  • 2021-10-07
  • 2018-12-09
  • 2020-04-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多