具有多个批次读取和放大功能的云功能http请求写

时间:2018-05-30 15:54:39

标签: node.js firebase google-cloud-firestore google-cloud-functions

我有一个由http请求触发的云功能,它打算执行以下操作:

  1. 根据查询获取一定数量的文档。
  2. 对于查询的每个文档执行读取操作。
  3. 从(2)获取新文档后,执行一些读/写操作(从子集合中删除,将文档添加到另一个子集合,并更新根集合上的文档)。
  4. 因此我需要等待(2)和(3)循环的东西然后执行批处理操作。

    下面是我目前的代码,当我在本地测试函数时,它正在工作。但是我无法将其部署到Firebase,因为它承诺会出现错误,例如"然后必须返回一个承诺"和#34;避免嵌套承诺"。

    exports.finishEvents =  functions.https.onRequest((req, res) => {
      const eventsRef = admin.firestore().collection('events');
      var currentTime = new Date().getTime();
      var currentTimeMinus1h = currentTime - 3600000;
    
      console.log('----- finishEvents started -----')
    
      const queryRef = eventsRef.where('finished', '==', false).where('date', '<=', new Date(currentTimeMinus1h)).get().then(function(querySnapshot){
        if (querySnapshot.size > 0) {
            querySnapshot.forEach(function(doc) {
    
              var owner_id = doc.data().owner_id;
              var event_id = doc.id;
              console.log(owner_id, event_id);
    
              var userEventOwnerGoingRef = admin.firestore().collection("user_events").doc(owner_id).collection('going').doc(event_id);
              userEventOwnerGoingRef.get().then(doc2 => {
                if (!doc2.exists) {
                  console.log('No such document!');
                } else {
                  console.log('Document data:', doc2.data());
                  var goingIds = doc.data().going_ids;
                  console.log('GOING IDS', goingIds);
                  var batch = admin.firestore().batch();
                  for (var userId in goingIds) {
                    if (goingIds.hasOwnProperty(userId)) {
                      console.log(userId + " -> " + goingIds[userId]);
                      var eventRef = admin.firestore().collection("events").doc(event_id);
                      var userEventGoingRef = admin.firestore().collection("user_events").doc(userId).collection('going').doc(doc2.id);
                      var userEventAttendedRef = admin.firestore().collection("user_events").doc(userId).collection('attended').doc(doc2.id);
                      batch.set(userEventAttendedRef, doc2.data());
                      batch.delete(userEventGoingRef)
                      if (userId == doc2.data().owner_id) batch.update(eventRef, {finished: true});
                    }
                  }
                  batch.commit().then(function () {
                    return res.status(200).send("Done.");
                  });
                }
              })
             .catch(err => {
               console.log('Error getting userEventOwnerGoingRef', err);
               return res.status(200).send("Finished.");
             });
           });
        } else {
            console.log("No events found");
            return res.status(200).send("Finished.");
        }
      })
      .catch(err => {
          console.log('Error getting events', err);
          return res.status(200).send("Finished.");
      });
    });
    

    当我在本地测试时,即使作业已完成,我也会收到错误消息

    UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Can't set headers after they are sent.
    

    我可以看到我正在为原始查询的每个文档发送结果,我只需要发送一次结果来完成云功能。

    我想我需要返回承诺,然后在完成步骤(2)和(3)之后执行我的批处理事务。然而,这是我第一次使用JavaScript,我正在努力解决这个问题。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:4)

当您的HTTPS函数发送了响应但忘记返回在达到超时限制之前解析的承诺时,会遇到Unhandled promise rejection错误。这意味着您不会在HTTPS函数中返回所有承诺。您的代码应如下所示:

exports.finishEvents =  functions.https.onRequest((req, res) => {
  const eventsRef = admin.firestore().collection('events')
  const currentTime = new Date().getTime()
  const currentTimeMinus1h = currentTime - 3600000

  console.log('----- finishEvents started -----')

  const queryRef = eventsRef
    .where('finished', '==', false)
    .where('date', '<=', new Date(currentTimeMinus1h))

  return queryRef.get().then((querySnapshot) => {
    // Use Promise.all with snapshot.docs.map to combine+return Promise context
    return Promise.all(querySnapshot.docs.map((doc) => {
      const owner_id = doc.get('owner_id')
      const event_id = doc.id
      console.log(owner_id, event_id)

      const userEventOwnerGoingRef = admin.firestore()
        .collection("user_events").doc(owner_id)
        .collection('going').doc(event_id)
      return userEventOwnerGoingRef.get().then((doc2) => {
        if (!doc2.exists) {
          console.log('No such document!')
          return
        } else {
          console.log('Document data:', doc2.data())
          const goingIds = doc.get('going_ids')
          console.log('GOING IDS', goingIds)
          const batch = admin.firestore().batch()
          for (const userId in goingIds) {
            if (goingIds.hasOwnProperty(userId)) {
              console.log(userId + " -> " + goingIds[userId])
              const eventRef = admin.firestore().collection("events").doc(event_id)
              const userEventGoingRef = admin.firestore()
                .collection("user_events").doc(userId).collection('going').doc(doc2.id)
              const userEventAttendedRef = admin.firestore()
                .collection("user_events").doc(userId).collection('attended').doc(doc2.id)
              batch.set(userEventAttendedRef, doc2.data())
              batch.delete(userEventGoingRef)
              if (userId == doc2.get('owner_id')) {
                batch.update(eventRef, {finished: true})
              }
            }
          }
          return batch.commit()
        }
      })
    }))
  })
  .then(() => {
    return res.status(200).send('Done.')
  })
  .catch((err) => {
    console.error(err)
    return res.status(200).send('Finished.')
  })
})

从这一点来看,重要的是不要束缚你的任何承诺。无论是通过将它们添加到数组还是等待它们全部解析/拒绝,或者通过从它们的范围/函数返回它们,始终保持它们的句柄。我希望这会有所帮助。

相关问题