Solicitud http de la función de nube con varias lecturas y escrituras por lotes

Tengo una función de nube activada por una solicitud http que intenta hacer lo siguiente:

  1. Obtener una cierta cantidad de documentos basados ​​en una consulta.
  2. Para cada documento de la consulta realice una operación de lectura.
  3. Después de obtener el nuevo documento de (2), realice algunas operaciones de lectura / escritura (eliminar de una subcolección, agregue el documento a otra subcolección y actualice un documento en una colección raíz).

Por lo tanto, necesito algo que espere el bucle de (2) y (3) y luego realice una operación por lotes.

A continuación se muestra el código que tengo en este momento y está funcionando cuando pruebo la función localmente. Sin embargo, no puedo implementarlo en Firebase ya que tiene errores de promesas como “cada uno debe devolver una promesa” y “evitar las promesas de anidación”.

exports.finishEvents = functions.https.onRequest((req, res) => { const eventsRef = admin.firestre().collection('events'); var currentTime = new Date().getTime(); var currentTimeMinus1h = currentTime - 3600000; console.log('----- finishEvents started -----') const queryRef = eventsRef.where('finished', '==', false).where('date', ' 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.firestre().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.firestre().batch(); for (var userId in goingIds) { if (goingIds.hasOwnProperty(userId)) { console.log(userId + " -> " + goingIds[userId]); var eventRef = admin.firestre().collection("events").doc(event_id); var userEventGoingRef = admin.firestre().collection("user_events").doc(userId).collection('going').doc(doc2.id); var userEventAttendedRef = admin.firestre().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."); }); }); 

Cuando lo pruebo localmente, a pesar de que el trabajo se ha completado, aparece un error que indica que

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

Puedo ver que estoy enviando el resultado para cada documento de la consulta original, y solo necesitaría enviar el resultado una vez para finalizar la función de nube.

Supongo que necesito devolver promesas y luego, después de completar los pasos (2) y (3), realizar mi transacción por lotes de todo. Sin embargo, es la primera vez que estoy usando javascript y estoy luchando con esto. Cualquier ayuda sería apreciada.

El error de Unhandled promise rejection se encuentra cuando la función HTTPS ha enviado una respuesta pero se olvidó de devolver una promesa que se resuelve antes de que se haya alcanzado el límite de tiempo de espera. Esto significa que no está devolviendo todas sus promesas en la función HTTPS. Su código debe verse algo como esto:

 exports.finishEvents = functions.https.onRequest((req, res) => { const eventsRef = admin.firestre().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.firestre() .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.firestre().batch() for (const userId in goingIds) { if (goingIds.hasOwnProperty(userId)) { console.log(userId + " -> " + goingIds[userId]) const eventRef = admin.firestre().collection("events").doc(event_id) const userEventGoingRef = admin.firestre() .collection("user_events").doc(userId).collection('going').doc(doc2.id) const userEventAttendedRef = admin.firestre() .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.') }) }) 

Lo importante a tomar de esto es no encajar ninguna de sus promesas. Manténgalos siempre controlados, ya sea agregándolos a una matriz y esperando que todos se resuelvan / rechacen, o devolviéndolos desde sus ámbitos / funciones. Espero que esto ayude.