异步/等待内部承诺

时间:2018-07-17 05:23:26

标签: reactjs firebase promise async-await firebase-storage

我想在将一个或多个文件上传到Firebase Storage之后更新数据库中的数据。我在上载任务的完整功能上使用了async关键字,并使用了await将带有downloadURL的新对象推送到了filesArr

但是结果不是我期望的。

enter image description here

我对async / await的使用未导致预期的行为。 Promise.all中的代码在将所有对象推送到filesArr之前执行。有人可以帮我吗?

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

2 个答案:

答案 0 :(得分:1)

这可能不是实际答案,但可以帮助您找到实现所需目标的方法:

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

我用forEach替换了您的map函数,该函数可以返回承诺。并将其放在Promise.all中,这样在他们都拿到filesArr之后,您将继续进行下一部分。

您在代码中使用async/await的方式是不正确的。 await实际上正在等待承诺的解决,而您没有Promise.resolve,并且您正在同时使用await.then(),我不知道为什么! 检查一下:  let downloadURL = await uploadTask.snapshot.ref.getDownloadURL(); 要么  uploadTask.snapshot.ref.getDownloadURL().then(downloadURL=>...)

希望这对您有所帮助。

答案 1 :(得分:0)

我认为您只需要按如下承诺减少

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

解决当前请求的回调函数。

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