Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'm using FileReader in a Vue.js project, and I have a problem with this code:

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));

This is the desired execution order (example with two files):

  • (wait 3s)
  • THEN block (file 1)
  • (wait 3s)
  • THEN block (file 2)
  • COMPLETED
  • But this is the actual execution order:

  • COMPLETED
  • (wait 3s)
  • THEN block
  • THEN block
  • The "THEN block" is correctly executed after the timeout, but the execution of the code in the for loop continues without waiting the execution of the onload function.

    How can I make the reader asynchronous? I tried many solutions (such as wrapping the for loop in a promise and putting the resolve() function inside .then()) but none of them works.

    Why read the file content into memory and upload them as base64? that is the worst. ~3 more bandwidth unnecessary processing, not enough RAM to handle large files. just append the files onto a FormData and upload that instead as a application/multipart-formdata – Endless May 11, 2021 at 10:18

    I'd recommend to "promisify" the Reader thing and then use Promise.all until all the files are uploaded.

    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;
                    Thank you, this works! I have one more problem now. I replaced the timeout with the actual upload code, axios.post(...), and I need to execute the /* CATCH block */ in the case that the post should fail. I tried with axios.post(...).then(...).catch((err) => Promise.reject(err)) but I think that it's wrong and it does not work with your Promise.all(). Do you have suggestions? Thank you!
    – Taekwondavide
                    May 11, 2021 at 11:41
                    Nice, please consider marking as resolved. You could wrap const response = await this.submitFile(...) in a try/catch block and call reject in the catch.
    – sandrooco
                    May 11, 2021 at 12:17
    

    Try this.

    Add await before the reader.onload it will hold until then or catch block execute successfully

    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));
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.