Thore
Thore

Reputation: 1838

Async/await inside promise

I want to update data in the database after one or multiple files has been uploaded to Firebase Storage. I used the async keyword on the complete function of the uploadTask and await to push the new objects with the downloadURL to filesArr.

However the result is not what I expected.

enter image description here

My usage of async/await didn't result in the expected behavior. The code inside Promise.all is executed before all the objects were pushed to filesArr. Can someone help me out?

onSubmitInvoiceData = () => {
    const { files } = this.state;
    if(files.length < 1) this.setState({ isLoading: false, errors: {...this.state.errors, general: ['Gelieve minstens één factuur te uploaden']} });
        else {
        const promises = [];
        const filesArr = [];

        files.forEach(file => {
            const uploadTask = Storage.ref(`session-123/files/${file.name}`).put(file);
            promises.push(uploadTask);

            uploadTask.on('state_changed', snapshot => {
                const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
            }, error => { console.log(error) }, async () => {
                await uploadTask.snapshot.ref.getDownloadURL().then(downloadURL => {
                    console.log('url: ', downloadURL);
                    filesArr.push({id: file.id, name: file.name, url: downloadURL});
                });
            });
        });

        Promise.all(promises).then(tasks => {
            console.log('tasks: ', tasks);
            Database.ref(`sessions/123`).update({
                lastUpdate: Firebase.database.ServerValue.TIMESTAMP,
                files: filesArr
            }).then(() => this.setState({ isLoading: false }))
        });
    }
}

Upvotes: 0

Views: 2263

Answers (2)

Omid Navy
Omid Navy

Reputation: 302

This may not be the actual answer but it may helps you to find out the way of achieving what you want:

onSubmitInvoiceData = () => {
const { files } = this.state;
    if(files.length < 1) this.setState({ isLoading: false, errors: {...this.state.errors, general: ['Gelieve minstens één factuur te uploaden']} });
        else {
        const promises = [];

        Promise.all(
            files.map(file => {
                return new Promise((resolve,reject)=>{

                    const uploadTask = Storage.ref(`session-123/files/${file.name}`).put(file);
                    promises.push(uploadTask);

                    uploadTask.on('state_changed', snapshot => {
                        const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
                    }, error => { console.log(error) }, () => {
                        uploadTask.snapshot.ref.getDownloadURL().then(downloadURL => {
                            console.log('url: ', downloadURL);
                            return resolve ({id: file.id, name: file.name, url: downloadURL});
                        });
                    });

                })
            })
        ).then(filesArr=>{
            Promise.all(promises).then(tasks => {
                console.log('tasks: ', tasks);
                Database.ref(`sessions/123`).update({
                    lastUpdate: Firebase.database.ServerValue.TIMESTAMP,
                    files: filesArr
                }).then(() => this.setState({ isLoading: false }))
            });
        })
    }    
}

I replaced your forEach function with map which can returns a promise. and put it inside a Promise.all so after they all got their filesArr, you will continue to next part.

The way you treated with async/await in your code is not correct. while await actually waiting for a promise to resolve, you have no Promise.resolve, and you are using await and .then() together which i can't understand why! check this: let downloadURL = await uploadTask.snapshot.ref.getDownloadURL(); OR uploadTask.snapshot.ref.getDownloadURL().then(downloadURL=>...)

I hope this can help you.

Upvotes: 1

Manoz
Manoz

Reputation: 6587

I think you will just need reduce with promise as follows

  files.reduce((promiseChain, item, index) => {
    return promiseChain.then(() => new Promise((resolve) => {
      //here item is one of the items in files array.
      this.myFunction(item, resolve);
    }));
  }, Promise.resolve()).then((tasks) => {
    //tasks
  });

Callback function which resolves current request.

 myFunction(item, resolve) {
        setTimeout(() => {
          //do something with item, alongside send resolve when it is finished.
          resolve(item);
        }, 100);
    }

Upvotes: 0

Related Questions