同时从多个承诺中返回Cloud Firebase数据

时间:2019-03-06 20:07:41

标签: javascript promise google-cloud-firestore google-cloud-functions

我仍在努力更好地理解诺言。我从道格·史蒂文森(Doug Stevenson)的YouTube视频中获得了一些关于诺言的示例,然后对其进行了修改以使用我的收藏。该代码与使用区域和城市的样本最相似。

代码如下:

exports.getMatchesNew = functions.https.onCall((data, context) => {
  console.log("In On Call", data);
  return db.collection('matches').get()
  .then(areaSnapshot => {
      const promises = [];
      areaSnapshot.forEach(doc => {
        var area = doc.data();
        // console.log("Area  is ", area.mentees);
        // console.log("area ID is ", area.id);

        // Loop through each mentee for a mentor
        for (const city in area.mentees)
        {
          // console.log("City is ", area.mentees[city]);

          // User Information for current mentee
          const p = db.collection('users').doc(area.mentees[city]).get();

          //User Information for current mentor
          const p2 = db.collection('users').doc(doc.id).get();
          //console.log("Doc ",p);
          // would like to combine this together, which will end up on one row
          // mentor name, mentee name
          promises.push(p, p2);
        }
      })
      
      return Promise.all(promises);
      //response.send(data);
  })
  .then(citySnapshots => {
    const results = [];
    citySnapshots.forEach(citySnap => {
      const data = citySnap.data();
      // this log entry is occuring once for each p and p2 from above.
      // I tried an array reference like citySnap[0] for mentee info and citySnap[1] for mentor info, this did not work.
      console.log("cSnap is: ", data);
      results.push(data);

    })
    return Promise.all(results);
  })
  .catch(error => {
    // Handle the error
    console.log(error);
    //response.status(500).send(error);
  });
});

输出是我获得了受训者的名字和姓氏,然后得到了导师的名字和姓氏的输出(在单独的行上)。

在Firestore中,matches集合中的每个文档都是指导者的ID和指导者ID的数组。所有用户信息都存储在“用户”集合中。 因此,我试图遍历每个匹配文档,并为每个导师/受训者组合生成一行数据。

当p和/或p2不可用时,我仍然需要添加一些处理。

我对“ p”和“ p2”的初衷是:

  1. 返回p的名字和姓氏,将其重命名为menteeFirstName和menteeLastName

  2. 返回p2的名字和姓氏,将其重命名为mentorFirstname和mentorLastName

  3. 合并此信息,并返回mentorFirstName,mentorLastName,menteeFirstName,menteeLastName的数组。

但是,我因此而掉进了兔子洞。我决定将其缩减为工作代码,然后发布。

那么,我可以合并“ p”和“ p2”中的数据吗?还是我做错了方法?

我来自关系数据库背景,因此带有异步调用的Firestore集合/文档概念对我来说是一个新概念,我对此越来越熟悉(但还不够)。

我试图理解其中的各种示例,但是我认为我现在对承诺的不熟悉是一个主要障碍。 我已经尝试了漫游者和史蒂文·萨克的建议。 漫游者的建议不会出错,但我相信它正在放弃承诺。

exports.getMatchesNew = functions.https.onCall((data, context) => {
  return db.collection('matches').get()
  .then(areaSnapshot => {
      const promises = [];
      areaSnapshot.forEach(doc => {
          var area = doc.data();
          for (let city in area.mentees) {
              const p = db.collection('users').doc(area.mentees[city]).get();
              const p2 = db.collection('users').doc(doc.id).get();
              promises.push(
                  Promise.all([p, p2]) // aggregate p and p2
                  .then(([mentee, mentor]) => {
                    var mentorInfo = mentor.data();
                    var menteeInfo = mentee.data();

                      console.log("Mentor: ", mentorInfo.lastName);
                      console.log("mentee: ", menteeInfo.lastName);
                      // return a plain object with all the required data for this iteration's doc
                      return {
                          // 'area': area, // for good measure
                          // 'city': city, // for good measure
                          'mentee': menteeInfo, // ???
                          'mentor': mentorInfo // ???
                      };
                  })
              );
          }
      })
      return Promise.all(promises);
  })
  .catch(error => {
      console.log(error);
      //response.status(500).send(error);
  });
});

我在日志中看到了数据,但是没有返回记录(或者我在.vue页面中错误地引用了它们

<template slot="mentorLastName" slot-scope="row">
          {{row.item.mentor.lastName}}
        </template>
        <template slot="menteelastName" slot-scope="row">
          {{row.item.mentee.lastName}}
        </template>

这在结果包含这些相同对象的其他情况下有效。

Steven Sark的代码也基于日志文件运行,没有错误。不同之处在于,vue页面从不返回(始终显示为“正在思考”)。过去,这意味着云功能存在错误。云功能日志中没有错误,并且控制台功能日志中未显示数据(而在Roamer版本中)。所以我不能证明这是可行的。

exports.getMatchesNew = functions.https.onCall((data, context) => {
  console.log("In On Call", data);
  return db.collection('matches').get()
  .then(areaSnapshot => {
      const promises = [];
      areaSnapshot.forEach(doc => {
        var area = doc.data();
        // console.log("Area  is ", area.mentees);
        // console.log("area ID is ", area.id);

        // Loop through each mentee for a mentor
        for (const city in area.mentees)
        {
          // console.log("City is ", area.mentees[city]);

          // User Information for current mentee
          const p = db.collection('users').doc(area.mentees[city]).get();

          //User Information for current mentor
          const p2 = db.collection('users').doc(doc.id).get();
          //console.log("Doc ",p);
          // would like to combine this together, which will end up on one row
          // mentor name, mentee name
          promises.push(p, p2);
        }
      })

      return Promise.all(promises);
      //response.send(data);
  })
  .then(citySnapshots => {

    let mentee = citySnapshots[0];
    let mentor = citySnapshots[1];
    console.log("Mentor: ", mentor.lastName);
    return {
      mentee,
      mentor
    };

  })
  .catch(error => {
    // Handle the error
    console.log(error);
    //response.status(500).send(error);
  });
});

我查看了这两个修改后的示例,觉得自己很了解,然后在没有得到结果时打sm自己。我觉得这两个都基于日志条目放弃了诺言,但是我不知道怎么做。在我看来,这些链接或链接在一起。那不是一个人。

3 个答案:

答案 0 :(得分:0)

已移至答案,因为它太大了,无法发表评论:

我从来没有遇到过任何使用Promise.all的示例(使用数据数组),但是也许这是来自诸如Bliebird之类的较早Promise库的示例,但不适用不再。

这是一个简单的示例:

Promis.all([ Promise.resolve('one'), Promise.reject(new Error('example failure') ])
.then( ( values ) => {
  //values[0] = data from promise 1; 'one'
  //values[1] = data from promise 2
})
.catch( ( error ) => {
  // note that an error from any promise provided will resolve this catch
})

编辑:

根据要求,将您的代码修改为使用Promise.all:

exports.getMatchesNew = functions.https.onCall((data, context) => {
  console.log("In On Call", data);
  return db.collection('matches').get()
  .then(areaSnapshot => {
      const promises = [];
      areaSnapshot.forEach(doc => {
        var area = doc.data();
        // console.log("Area  is ", area.mentees);
        // console.log("area ID is ", area.id);

        // Loop through each mentee for a mentor
        for (const city in area.mentees)
        {
          // console.log("City is ", area.mentees[city]);

          // User Information for current mentee
          const p = db.collection('users').doc(area.mentees[city]).get();

          //User Information for current mentor
          const p2 = db.collection('users').doc(doc.id).get();
          //console.log("Doc ",p);
          // would like to combine this together, which will end up on one row
          // mentor name, mentee name
          promises.push(p, p2);
        }
      })

      return Promise.all(promises);
      //response.send(data);
  })
  .then(citySnapshots => {

    let mentee = citySnapshots[0];
    let mentor = citySnapshots[1];
    return {
      mentee,
      mentor
    };

  })
  .catch(error => {
    // Handle the error
    console.log(error);
    //response.status(500).send(error);
  });
});

另一个没有您的数据库逻辑的示例,我不太了解:


var response1 = { "foo": "bar" }
var response2 = { "biz": "baz" }


  return Promise.resolve( [ response1, response2 ] )
  .then(areaSnapshot => {
      const promises = [];
      areaSnapshot.forEach(doc => {
          promises.push(Promise.resolve( doc ));
      })

      return Promise.all(promises);
      //response.send(data);
  })
  .then(citySnapshots => {

    let mentee = citySnapshots[0];
    let mentor = citySnapshots[1];
    return {
      mentee,
      mentor
    };

  })
  .catch(error => {
    // Handle the error
    console.log(error);
    //response.status(500).send(error);
  });


答案 1 :(得分:0)

您的方法可以起作用,但由于包含promisesp的一个p2数组的形成而使您的方法变得混乱。

据我所知,您希望将每个文档所需的数据捆绑到一个对象中。

有很多方法可以做到这一点。这是一个使用内部Promise.all()来聚合每个[p,p2]对诺言,然后形成所需对象的人。

exports.getMatchesNew = functions.https.onCall((data, context) => {
    return db.collection('matches').get()
    .then(areaSnapshot => {
        const promises = [];
        areaSnapshot.forEach(doc => {
            var area = doc.data();
            for (let city in area.mentees) {
                const p = db.collection('users').doc(area.mentees[city]).get();
                const p2 = db.collection('users').doc(doc.id).get();
                promises.push(
                    Promise.all([p, p2]) // aggregate p and p2
                    .then(([mentee, mentor]) => {
                        // return a plain object with all the required data for this iteration's doc
                        return {
                            'area': area, // for good measure
                            'city': city, // for good measure
                            'mentee': mentee.data(), // ???
                            'mentor': mentor.data() // ???
                        };
                    });
                );
            }
        })
        return Promise.all(promises);
    })
    .catch(error => {
        console.log(error);
        //response.status(500).send(error);
    });
});

所有假设.get()是异步的并且.data()是同步的

返回的promise的成功路径将传递一组{area,mentee,mentor}个对象。

由于我不完全了解数据,可能不是100%正确。应该不难适应。

答案 2 :(得分:0)

这是最终的工作代码。感谢@ Roamer-1888和“ @Steven Stark”

最终的代码有点像是两者提供的解决方案之间的交叉。非常感谢您的帮助。

因此,我对诺言更加满意。希望我能与他们合作得更多一些,以使他们感到更舒适,并增加保留它的机会。

// lets call this Integer reference ABC101
Integer i = new Integer(5);

//j points to i so j has the reference ABC101
Integer j = i;

//creates a new Integer instance for j?
j++;
//I want j++ to edit and return ABC101
System.out.println(i); // 5 not 6