-2

I am using Angular + Firebase for a simple web app. I have this function:

private async uploadFile(id: number, filePath: string, name: string, input: HTMLInputElement) : Promise<string> {
    const ext = filePath.split('.').pop();
    const path = '/' + id + '/' + name + '.' + ext
    const fileRef = ref(this.firebaseStorage, path);
    await uploadBytesResumable(fileRef, input.files[0]);
    let url: string = '';
    await getDownloadURL(fileRef).then((result: string) => {
      url = result;
    });
    console.log(url);
    return url;
  }

in which I want to upload a file in Firebase and return the string path (i.e. public url) of the image after it has been uploaded.

The problem is that the getDownloadURL(fileRef) returns a Promise and not a string, and I can't, for the life of me, figure out how to wait and unpack the promise to a string so that uploadFile can return a string value and not a promise!

I tried using the await keyword but the compiler still says it returns a Promise! What's the point then?

Edit: This is how I use uploadFile:

    if (thumbnailStr != '') {
      thumbnailStr = this.uploadFile(id, this.blogForm.get('thumbnail').value, 'thumbnail', thumbnailImg);
    }
    if (backgroundStr != '') {
      backgroundStr = this.uploadFile(id, this.blogForm.get('background').value, 'background', backgroundImg);
    }
    console.log('url:', thumbnailStr);
    const blog = {
      'id': id,
      'blogDate': datetime,
      'heading': this.blogForm.get('heading').value,
      'subHeading': this.blogForm.get('subHeading').value,
      'blogDetail': this.blogForm.get('blogDetail').value,
      'thumbnail': thumbnailStr,
      'background': backgroundStr
    };
    this.blogs_service.addBlog(blog);

Edit #2: I figured it out. Making all functions asynchronous worked:

  private async uploadFile(id: number, filePath: string, name: string, input: HTMLInputElement) : Promise<string> {
    const ext = filePath.split('.').pop();
    const path = '/' + id + '/' + name + '.' + ext
    const fileRef = ref(this.firebaseStorage, path);
    await uploadBytesResumable(fileRef, input.files[0]);
    return await getDownloadURL(fileRef);
  }

  async addPost(thumbnailImg: HTMLInputElement, backgroundImg: HTMLInputElement) {
    ...
    if (thumbnailStr != '') {
      thumbnailStr = await this.uploadFile(id, this.blogForm.get('thumbnail').value, 'thumbnail', thumbnailImg);
    }
    if (backgroundStr != '') {
      backgroundStr = await this.uploadFile(id, this.blogForm.get('background').value, 'background', backgroundImg);
    }
    ...

P.S. I don't know why my question was marked as closed as I did not find the answer in the similar question but ok.

9
  • const url = await getDownloadURL(fileRef); should be all you need Commented Jan 3, 2024 at 14:44
  • I thought so too, but compiler says that url is a Promise<string> and not a string! Commented Jan 3, 2024 at 14:46
  • so that uploadFile can return a string value and not a promise! That's impossible. The value doesn't exist yet, so the best you can do is return a promise from uploadFile. Whatever code calls uploadFile will need to await that promise if it wants to know the value Commented Jan 3, 2024 at 14:46
  • Why is it impossible to block and wait for the value to become available, turning an async call to a sync call basically? Commented Jan 3, 2024 at 14:47
  • 1
    Why is it impossible to block and wait for the value to become available Javascript is, for the most part, single threaded. If you could block, you would not want to because it would lock up the entire browser. There is technically a function that does a synchronous http request, but getDownloadURL is (correctly) not written to use it. Since it doesn't use it, the code is asynchronous, and asynchronous code begets other asynchronous code. You can't take something that's asynchronous and make it synchronous in javascript. Commented Jan 3, 2024 at 14:52

1 Answer 1

2

How are you calling your uploadFile function? It's important to remember that since it's an async function, it will always return a Promise.

Instead of using the .then() method within your uploadFile function, you might consider directly returning the result of the awaited Promise. This approach simplifies the function by utilizing the async/await syntax more effectively.

It can look like this:

private async uploadFile(...): Promise<string> {
  // ...
  // Wait to get the URL
  return await getDownloadURL(fileRef);
}

Remember, when you call this uploadFile function elsewhere in your code, you must handle the returned Promise. :)

Sign up to request clarification or add additional context in comments.

5 Comments

Actually, i would like for uploadFile to be a syncronous function. I only added the async because I needed to to call await, which doesn't work in the way that I thought it did, i.e. turning an async call to a sync call. Is there another way to do this? I want uploadFile to be syncronous as shown by its usage in my edit.
It can't be synchronous since you're working with Promise, so you should handle it properly
Well, this kind of messes up my code... How do I do 2 async calls for example? In a nested way?
Do you want to do them sequentially or in parallel? For sequentially: const result1 = await callOne(); const result2 = await callTwo();. For parallel, const [result1, result2] = await Promise.all([callOne(), callTwo()]);
Ohh nice, the parallel will be even better. Thank you!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.