0

I'm having a hard time understanding why my code doesn't work and I need some help

onMount(async() => {

  // download the media file
    if (post.file_name) {
      await fetch(`/api/file?fileName=${post.file_name}`, {
        method: 'GET'
      })
      // convert the file into displayable image, video, or audio
      .then(async res => {
        const blob = await res.blob()
        if (blob.type.includes('image')) {
          mediaType = 'image'
          let reader = new FileReader()
          reader.readAsDataURL(blob);
          reader.onload = (event) => {
            media = event.target?.result
          }
        }
        else if (blob.type.includes('video')) {
          mediaType = 'video'
          media = URL.createObjectURL(blob)
        }
        else if (blob.type.includes('audio')) {
          mediaType = 'audio'
          // TODO implement this
        }
      })
      .catch(err => {
        console.error(err);
      })
    }

I then moved that code and added logging to it in a .ts file and call it from the same .svelte file

.ts file

export function getFileRepresentation(fileName: string) {

    let file: FileRepresentation = {
        string: '',
        type: undefined
    }

    fetch(`/api/file?fileName=${fileName}`, {
        method: 'GET'
    })
    // convert the file into displayable image, video, or audio
    .then(async res => {
        const blob = await res.blob()
        if (blob.type.includes('image')) {
            file.type = 'image'
            let reader = new FileReader()
            reader.readAsDataURL(blob);
            reader.onload = (event) => {
                file.string = event.target?.result
                console.log('in promise');
                console.log(file.string); // DEBUGGING
            }
        }
        else if (blob.type.includes('video')) {
            file.type = 'video'
            file.string = URL.createObjectURL(blob)
        }
        else if (blob.type.includes('audio')) {
            file.type = 'audio'
            // TODO implement this
        }
    })
    .catch(err => {
        console.error(err);
    })
    
    console.log('on return');
    console.log(file.string); // DEBUGGING

    return file
}

.svelte file

onMount(async() => {
   if (post.file_name) {
      const file = await getFileRepresentation(post.file_name)
      media = file.string
      mediaType = file.type
    }

The new code in the .ts file no longer works. The console.logs show me that the 'on return' one is called first, with file.string being an empty string, and then 'in promise' one being called second with the string having the correct value in it. Why is this code working differently here than in the .svelte file? It's all client side code so I'm really confused.

The specific code that doesn't work as expected is lines 53-59, but I noticed that lines 6-63 work, so there's something I'm not understanding with how FileReader.load() works

Edit: replaced screenshots with code snippets

Peyton Hanel
  • 374
  • 1
  • 3
  • 13

1 Answers1

1

In the Svelte file you update local state and the order of execution does not really matter; in the extracted code where you try to return an object, this is not the case.

The order is wrong, you first return the object and its contents are set later, asynchronously when the fetch is finished. Svelte will not be notified of these changes as the code is outside a component.

If you are using await anyway, then I would recommend using it consistently with the fetch as well. To wait for the file reader result, that part can be turned into a promise.

try {
  const res = await fetch(...);
  const blob = await res.blob();
  // ...
  return file;
}
catch (error: any) {
  // what used to be in `.catch(...)`
}

If you were to keep the Promise chains, you would have to return a Promise, namely where the async code starts and the last then should return the final file object.

return fetch(...)
  .then(res => {
    // ...
    return file;
  })

(You might also want to check the response code of the fetched result, there is a utility property ok that checks if it is in the 200-299 range.)

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • This still doesn't cause the FileReader onload function to run before the function returns – Peyton Hanel Apr 06 '23 at 00:48
  • You can easily [wrap the file reader in a promise](https://stackoverflow.com/questions/34495796/javascript-promises-with-filereader) to also `await` that part. – H.B. Apr 06 '23 at 00:53
  • If you edit your answer and add that part I will accept it as best answer. – Peyton Hanel Apr 06 '23 at 01:00
  • I already linked it in the answer when I added the comment. Copying the exact code here should not be necessary. – H.B. Apr 06 '23 at 01:02