【问题标题】:How to turn a function into async如何将函数变为异步
【发布时间】:2020-01-27 01:32:08
【问题描述】:

我首先要说我几乎没有构建异步函数的经验,在阅读了关于它的文档之后,我更加困惑。

我使用 vue.js 作为框架,我的方法调用 firebase。

目标

问题在于,使用 firebase 删除文档时,它不会自动删除所有嵌套文档,您必须使用递归函数手动执行此操作。所以我不得不重新创建删除函数来完成这个,并通过制作一个 mixin 来做到这一点。 mixin 现在可以工作了,但是,reload emit 事件现在会在 delete 函数完成之前触发,因此会在 DOM 中保留一个 ghost 元素。

我希望在 deleteProject() 中对 firebase 的所有调用都完成之前,$emit('reload') 不会触发。

我的代码

<script>
import { deleteProject } from '../../../mixins/deleteData'

export default {
  mixins: [deleteProject],
  props: ['yearId', 'projectId', 'project'],
  name: 'deleteProjectModal',
  data: () => ({
    dialog: false
  }),
  methods: {
    del () {
      // call deleteYear method from mixin
      this.deleteProject(this.yearId, this.projectId)
      // emit reload signal for database relaod
      this.$emit('reload')
      // close modal
      this.dialog = false
    }
  }
}
</script>
import firebase from '@/firebase/init'

export const deleteProject = {
  methods: {
    deleteProject (yearId, projectId) {
      // delete project, all documents in the images subcollection of the project, and all stored images
      firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).get()
        .then((doc) => {
          // delete thumbnail
          firebase.storage().ref(doc.data().thumbFullPath).delete()
          // delete project document
          firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).delete()
          firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).collection('images').get()
            .then((querySnapshot) => {
              querySnapshot.forEach((doc) => {
                // delete gallery image
                this.imagePaths.append(doc.data().fullPath)
                // delete reference document for gallery image
                doc.ref.delete()
              })
            })
        })
    }
  }
}

我的尝试

我认为解决方案将围绕将deleteProject() 转换为异步函数并调用deleteProject().then($emit('reload'))。但是,我一直无法弄清楚如何做到这一点。

编辑

根据要求,reload 触发以下函数:

import firebase from '@/firebase/init'

export const loadGallery = {
  data: () => ({
    years: []
  }),
  methods: {
    loadProjects () {
      var years = []
      var temp = []
      firebase.firestore().collection('years').orderBy('year', 'desc').onSnapshot((querySnapshot) => {
        querySnapshot.forEach((year) => {
          firebase.firestore().collection('years').doc(year.id).collection('projects').get().then((querySnapshot) => {
            querySnapshot.forEach((project) => {
              var objectMicro = { title: project.data().title, thumbUrl: project.data().thumbUrl, route: project.data().route, id: project.id }
              temp.push(objectMicro)
            })
          }).then(() => {
            var objectMacro = { year: year.data().year, id: year.id, projects: temp }
            years.push(objectMacro)
            temp = []
          })
        })
      })
      this.years = years
    }
  }
}

【问题讨论】:

  • 您好,您还有问题吗? IE。你还在寻求帮助吗?请回复@RenaudTarnec
  • @RenaudTarnec 我仍然没有找到解决问题的方法,所以是的,仍在寻求帮助。我目前的 hack 修复是创建一个按钮,一旦删除功能运行,用户可以按下该按钮手动触发 reload
  • 看看答案。

标签: javascript firebase vue.js async-await google-cloud-firestore


【解决方案1】:

您只需要在这个deleteProject 方法的Promise 链中返回最重要的 .then(),任何后续调用都应该能够从这一点获取它。

deleteProject() {
  // Add a return keyword here, or assign the Promise to a variable and return it. 
  return firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).get()
        .then( /* removed for brevity */ )
}

调用方法,

methods: {
  del() {
    this.deleteProject(this.yearId, this.projectId).then(() => {
      this.$emit('reload')
    })
  }
}

或者,使用 async 关键字使其“更漂亮”,

methods: {
  async del() {
    // You probably want to do try-catch here as well.
    await this.deleteProject(this.yearId, this.projectId);

    this.$emit('reload');
  }
}

【讨论】:

  • 感谢您的帮助,这个定义帮助我更好地理解异步。但是,这并没有解决我的问题。已删除文档的 ghost 元素仍保留在 DOM 中,只有在手动重新加载时才会将其删除。
  • 这个reload事件有什么作用?
  • 我已更新问题以显示重新加载功能
  • 我对 Firestore 不是很熟悉,但似乎问题确实不在 loadProjects 函数上,我怀疑图像被删除了,你能确认它是否做了它应该做的事情吗?至于 Promise awaiting 部分,上面的提示应该会为您指明正确的方向。
  • 无论如何感谢您的帮助,所有功能都正常工作,但它们似乎在不正确的时间触发。我在解决存储文件之前删除了 Firestore 文档时遇到了一些问题,因此我认为问题是 Firebase 没有按照我期望的方式返回完成任务的承诺。
【解决方案2】:

您正在 deleteProject 方法中执行多个异步 Firebase 操作(调用 Firestore get()、Firestore delete()、Cloud Storage delete(),...),但您没有正确链接这些返回的不同 Promise Firebase 方法。

您必须使用 then() 方法链接这些 Promise,该方法“返回一个允许方法链接的 Promise”。

所以,以下应该可以解决问题(但未经测试):

export const deleteProject = {
   methods: {     
      deleteProject (yearId, projectId) {
      // delete project, all documents in the images subcollection of the project, and all stored images
      return firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).get()
        .then((doc) => {
          // delete thumbnail
          return firebase.storage().ref(doc.data().thumbFullPath).delete()
        })
       .then(() => {
          return firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).delete();
       })
       .then(() => {
          return firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).collection('images').get()
       })
       .then((querySnapshot) => {
          const promises = [];
          querySnapshot.forEach((doc) => {
            // delete gallery image
            this.imagePaths.append(doc.data().fullPath)
            // delete reference document for gallery image
            promises.push(doc.ref.delete());
          })
          return Promise.all(promises);
       })
      }
  }
}

由于这个deleteProject() 方法现在是异步的,你需要在你的mixin 中调用它:

  methods: {
    del () {
      // call deleteYear method from mixin
      this.deleteProject(this.yearId, this.projectId)
      .then(() => {
         // emit reload signal for database relaod
         this.$emit('reload')
         // close modal
         this.dialog = false
      })
    }
  }

请注意,我们使用Promise.all() 并行执行forEach() 中的所有delete() 方法调用。 Promise.all() 方法返回一个单一的 Promise,当所有作为可迭代对象(即 promises 数组)传递的承诺都已实现时,该 Promise 将实现。

【讨论】:

  • 这个问题是删除操作现在只删除项目文档,而不是它的嵌套文档或图像。我会尝试重新安排deleteProject mixin 来解决这个问题并回复你。
  • 感谢成功,我只需将删除图像的最终承诺移到顶部。请问使用Promise.all() 有什么好处。我用的时候似乎比不用的时候慢(可能只是巧合)
  • @TomWalsh 我们使用Promise.all() 并行执行一组返回Promise 的异步操作。因为我们不知道我们不能预先链接querySnapshot 的大小,所以我们不能像处理其他方法一样用then() 方法链接它们。所以我们使用Promise.all() 来确保所有这些异步操作都已完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-29
  • 1970-01-01
  • 2012-07-25
  • 1970-01-01
相关资源
最近更新 更多