【问题标题】:firestore get data from two tables one after the other return empty arrayfirestore 一个接一个地从两个表中获取数据返回空数组
【发布时间】:2020-09-21 12:36:06
【问题描述】:

我正在使用 firebase - firestore。我有课程和任务集合。 我想从课程集合中获取用户的所有课程,并为每门课程从任务集合中获取天数数据,然后将所有这些数据保存在一个数组中。

  getData = () => {
    var arr = []
    f.auth().onAuthStateChanged(async (user) => {
       db.collection("courses")
        .where("uid", "==", user.uid)
        .get()
        .then((snapshot) => {

          var a = {};
          snapshot.forEach((doc) => {
            let coursesData =  doc.data()
            let courseName = coursesData.name;
            let kita = coursesData.kita;
            a = { name: courseName, id: doc.data().code, k: kita };
            let snapshotData = await db
              .collection("tasks")
              .where("uid", "==", user.uid)
              .where("name", "==", courseName)
              .where("kita", "==", kita)
              .get();

            let numActiveCourse = 0;
            snapshotData.forEach((dc) => {
              let taskData =  dc.data()
              console.log('taskData',taskData)
              let days = taskData.days;
              if (days > 0) {
                numActiveCourse = 1;
              }
            });

            a = { ...a, numActiveCourse };
            arr.push(a);
            console.log("arr2 is", arr);
          });
        })
        .catch((e) => {
          console.log("error is courses", e);
        });
        this.setState({data:arr})

    });

  };

问题是 arr 总是空的(我想我有异步问题) 并且快照完成后不会等待。

【问题讨论】:

  • 您是否尝试过调用getData 调用this.setState({data: this.getData()}) 并声明getData = async () => 箭头函数,该函数将在返回awaiting 之前返回arr
  • 是的,我试过了,还是不行。

标签: javascript firebase google-cloud-firestore


【解决方案1】:

我找到了解决方案。 问题是因为我试图将异步等待变为 forEach 并且它不等待回答。 解决办法是

 readCourses = async()=>{
f.auth().onAuthStateChanged(async (user) => {
  let loadedPosts = {};
  let docSnaps = await db.collection("courses").where("uid", "==", user.uid).get();
  for (let doc of docSnaps.docs){

    let courseName = doc.data().name;
    let kita = doc.data().kita
    loadedPosts[doc.id] = {
      ...doc.data(),
      k:kita,
      id:doc.data().code
    }
    const taskSnap =  await db
      .collection("tasks")
      .where("uid", "==", user.uid)
      .where("name", "==", courseName)
      .where("kita", "==", kita)
      .get()

    let numActiveCourse = 0
    for(let task of taskSnap.docs){
      let taskData = task.data()
      if(taskData.days>0){
        numActiveCourse =numActiveCourse+1
      }

    }
    loadedPosts[doc.id].numActiveCourse = numActiveCourse
  }
  console.log('loadedPosts',loadedPosts)
  this.setState({data:loadedPosts})

})

}

如果您有任何其他的解决方案,我想看看。

【讨论】:

    【解决方案2】:
    1. 在原始代码中混合awaitthen 不是一个好主意。你犯了第一个错误。不仅您没有等待forEach 的结果,而且this.setState({data:arr})then 之外,甚至在您到达forEach 调用之前就已执行。
    2. 您的初始代码版本的另一个问题是如您所说 - 不等待 forEach 的结果。但我不确定你是否完全理解它。因为您不必对代码进行太多更改(撇开可读性不谈)。您所要做的就是:
      // change db.collection("courses")...then(...) to
      const snapshot = await db.collection("courses")... // only now onAuthStateChanged callback becomes async
      ...
      // then change forEach() to map() and wait for result
      const promises = snapshot.map(async (doc) => { ... })
      await Promise.all(promises)
      ...
      
    3. 至于你的新版本:在for 循环的每次迭代中你await。这意味着对每个taskSnap 的请求将一个接一个 执行。那很糟。特别是在慢速连接上。查看 sn-p(我已将其简化到最低限度):getDatamap 在 1 秒内完成,for 版本在 4 秒内完成。 (而且您还从新代码中删除了 catch - 这不是一个好主意。)

    let i = 0
    const get_courses = () => new Promise((resolve) => setTimeout(() => resolve(["a","b","c","d"]), 10))
    const get_tasks   = () => new Promise((resolve) => setTimeout(() => resolve(++i), 1000))
    const f_auth_onAuthStateChanged = fn => fn()
    
    const getData = () => {
        const data = []
        f_auth_onAuthStateChanged(async (user) => {
          try {
            const courses = await get_courses()
            const promises = courses.map(async (course) => {
              const tasks = await get_tasks()
              data.push({ course, tasks })
            })
            await Promise.all(promises)
            console.log(data) // this.setState({ data })
            console.timeEnd("map")
          } catch(e) { console.error(e) }
      })
    }
    console.time("map")
    getData()
    
    const getData2 = () => {
        const data = []
        f_auth_onAuthStateChanged(async (user) => {
          try {
            const courses = await get_courses()
            for (const course of courses) {
              const tasks = await get_tasks()
              data.push({ course, tasks })
            }
            console.log(data) // this.setState({ data })
            console.timeEnd("for")
          } catch(e) { console.error(e) }
      })
    }
    console.time("for")
    getData2()
    1. 您自己的答案中的readCourses 函数不会返回Promise。所以正式它不是async。除了小的代码可读性改进之外,这不会改变任何事情。来自原始代码的 onAuthStateChanged 回调也是如此。

    【讨论】:

      猜你喜欢
      • 2017-06-11
      • 2019-05-06
      • 1970-01-01
      • 1970-01-01
      • 2011-10-10
      • 1970-01-01
      • 1970-01-01
      • 2021-01-06
      • 2012-01-17
      相关资源
      最近更新 更多