-1

I am working with React backed with Firebase. I am using an asynchronous firebase function to get the download URL for a file that I add. This function runs multiple times inside a for loop and to keep everything synchronized I use 'async' and 'await' to wait for the file to be added before moving on to the next entry in the loop. But the array, 'uploads', that I am populating, in the end, comes out empty after this entire loop is done. Is it not working because await is inside of then. If not, how can I do this?

 _handlePost = () => {
    var fs = this.props.firebase.getFS();
    let uploads = [];

    // if any files attached to post
    if (this.state.allFiles.length > 0) {
        const currentComponent = this;
        let files = this.state.allFiles;
        for (let i=0; i<files.length; i++) {
            var file = files[i];
            var type = file.type;
            var file_parts = type.split("/");
            var type = "." + file_parts[1];
            var cat = file_parts[0];
            var file_path = '';

            // save to correct firebase storage folder
            if (cat === "image")
                file_path = "image/";
            else if (cat === "video")
                file_path = "videos/";
            else
                file_path = "test_file_upload";
            const uploadtask = currentComponent.props.firebase.getStorageRef(file_path + file.name).put(file);
            uploadtask.on(
                'state_changed',
                null,
                (error) => { console.log(error); },
                () => {
                    currentComponent.props.firebase.getStorageRef(file_path).child(file.name).getDownloadURL()
                    .then(function(url) {
                        // add to content collection
                        fs.collection("content").add({
                            category: cat,
                            file_name: file.name,
                            file_type: type,
                            time_uploaded: new Date(Date.now()),
                            user_id: currentComponent.props.userId,
                            url_link: url
                        })
                        .then(async function(doc) {
                            console.log("Added to content collection!");
                            const attachment = {
                                category: type,
                                content_id: doc.id,
                                url_link: url
                            };
                            uploads.push(attachment);
                        });
                    });    
                }
            );
        };
    }
    console.log("Done");
    console.log(uploads);
    // add wall_posts to database
    fs.collection("wall_posts").add({
        attachments: uploads,
        body_text: this.state.post_text,
        posterid: this.props.userId,
        time_posted: new Date(Date.now()),
        user_id_wall: this.props.userId
    })
    .then(function() {
        console.log("Post successful!");
    });
  }
  • Why mix chaining and awaiting? And what do you mean by *"comes out empty"*? – jonrsharpe Oct 02 '19 at 07:19
  • @jonrsharpe I was only chaining at first. What I mean is that the 'uploads' array does not get populated after the promise is done. It is done later after other functions that follow this code. So, I tried to do async/await as well to see if that works. – Sanjiv Pradhanang Oct 02 '19 at 07:23
  • please also post the `for ` loop that wraps this function call. Even the whole function, that wraps the `for` loop would be useful – Tudor Constantin Oct 02 '19 at 07:23
  • And how exactly do you determine *"after the promise is done"*? Give a [mcve]. – jonrsharpe Oct 02 '19 at 07:24
  • @jonrsharpe I printed out on the console, and also the code that runs after shows no data on the firebase database. I have added the code in the question. – Sanjiv Pradhanang Oct 02 '19 at 07:30
  • Of course it's not populated there. See the canonical https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call – jonrsharpe Oct 02 '19 at 07:31
  • Ok, I tried to make my code better again. From here, I am gonna try all the suggestions by you guys and the guys who answered and get back to y'all. – Sanjiv Pradhanang Oct 02 '19 at 08:09
  • Thanks to all again. Turns out, the solution was to move the code block at the end that adds to the collection into the "then" block of the async function itself. – Sanjiv Pradhanang Oct 05 '19 at 16:48

2 Answers2

0

Use the for await ... of statement, instead of whatever else you're using for looping. This will allow the promise inside each iteration to resolve before moving on to the next.

// I guess from your code that you're iterating through a FileList? Could be an array or any sort of list.
for await (file of files) {
  // ...
}
gilbert-v
  • 1,263
  • 1
  • 11
  • 23
0

The problem is that the forEach( ... ) doesn't await for promises to finish: it iterates over your entries and starts n promises, which are not resolved by the time you fs.collection("wall_posts").add(...). I am rewriting your forEach() and function calls to make it function in a more idiomatic async/await style:

async function chooseAName(){
 let files = this.state.allFiles;
 for (let i = 0; i < files.length; i++){
      const file = files[i]; // this is an assumption
      const url = await  currentComponent.props.firebase
      .getStorageRef(file_path) //where is this set in your code? maybe it should be file.path?
      .child(file.name)
      .getDownloadURL();     

        // add to content collection
      const doc = await fs
          .collection('content')
          .add({
            category: cat,
            file_name: file.name,
            file_type: type,
            time_uploaded: new Date(Date.now()),
            user_id: currentComponent.props.userId,
            url_link: url,
          });

            console.log('Added to content collection!');
            const attachment = {
              category: type,
              content_id: doc.id,
              url_link: url,
            };
            uploads.push(attachment);

}
await fs.collection("wall_posts").add({
        attachments: uploads,
        body_text: this.state.post_text,
        posterid: this.props.userId,
        time_posted: new Date(Date.now()),
        user_id_wall: this.props.userId
    });

console.log("Post successful!");
Tudor Constantin
  • 26,330
  • 7
  • 49
  • 72