代码之家  ›  专栏  ›  技术社区  ›  b-fg

多批读写的云函数http请求

  •  7
  • b-fg  · 技术社区  · 7 年前

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

    1. 根据查询得到一定数量的文档。
    2. 对查询的每个文档执行读取操作。
    3. 从(2)获取新文档后,执行一些读/写操作(从子集合中删除、将文档添加到另一个子集合,以及更新根集合中的文档)。

    因此,我需要等待(2)和(3)循环,然后执行批处理操作的东西。

    下面是我目前拥有的代码,当我在本地测试函数时,这些代码正在工作。但是我不能将它部署到firebase,因为它有承诺错误,比如“每次都必须返回承诺”和“避免嵌套承诺”。

    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 回复  |  直到 7 年前
        1
  •  4
  •   Bryan Massoth    7 年前

    这个 Unhandled promise rejection 当您的https函数已发送响应但忘记返回在达到超时限制之前解析的承诺时,会遇到错误。这意味着您没有在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.')
      })
    })
    

    从这件事中得到的最重要的是不要违背你的任何承诺。无论是将它们添加到数组中并等待它们全部解析/拒绝,还是从它们的作用域/函数返回它们,始终对它们保持一个句柄。我希望这能有帮助。