Having a promise issue with my google cloud function

旧城冷巷雨未停 提交于 2020-01-16 18:01:05

问题


I have an http trigger in cloud functions that is working, however I am getting some logs come back in a weird order. I am pretty sure I have not executed the promises correctly.

here is the code:

exports.createGame = functions.https.onRequest((req, res) => {
    return cors(req, res, () => {

        // Check for POST request
        if (req.method !== "POST") {
            res.status(400).send('Please send a POST request');
            return;
        }
        const createGame = admin.firestore().collection('games')

        generatePin() 

        function generatePin() {
            ...pin generating code goes here...

            addGame(newGidToString)
        }

        function addGame(newGidToString) {
            var getGame = admin.firestore().collection('games')
            getGame = getGame.where('gid', '==', newGidToString)
            getGame.get().then(snapshot => {
                if (snapshot.empty) {

                    console.log('BODY ', req.body)

                    const promises = [];

                    const gameTitle = req.body.gametitle
                    const targetGID = req.body.target
                    const numPlayers = req.body.playercount.toString()

                    const newGID = newGidToString
                    const collections = ['games', 'clues', 'detours', 'messages', 'questions', 'tagstate']
                    collections.forEach(function (element) {
                        console.log('start loop', element)
                        let elementsRef = admin.firestore().collection(element)
                        elementsRef = elementsRef.where('gid', '==', targetGID)
                        elementsRef.get().then(snapshot => {
                            var batch = admin.firestore().batch()
                            snapshot.forEach(function (doc) {
                                // change the gid to the new game gid
                                let data = doc.data()
                                data.gid = newGID

                                if(data.template){
                                    data.template = false
                                }

                                if(!data.parent) {
                                    data.parent = targetGID
                                }

                                if (!data.playercount) {
                                    data.playercount = numPlayers
                                }

                                if (data.gametitle) {
                                    data.gametitle = gameTitle
                                }

                                let elementRef = admin.firestore().collection(element).doc()
                                batch.set(elementRef, data)
                            })
                            batch.commit().then(data => {
                                console.log('complete batch ', element)
                            })
                        })
                    })
                    res.send({ status: 200, message: 'Game: added.', pin: newGID})
                    console.log('completed');
                } else {
                    console.log('found a match ', snapshot)
                    // re-run the generator for a new pin
                    generatePin()
                }
            })
        }
    })

})

Here is a screen shot of the console logs.

Notice the order of the logs. It would seem logical that completed would come at the end fo the loops.

Any help is great appreciated.

UPDATE:

function addGame(newGidToString) {
            var getGame = admin.firestore().collection('games')
            getGame = getGame.where('gid', '==', newGidToString)
            getGame.get().then(snapshot => {
                if (snapshot.empty) {

                    console.log('BODY ', req.body)

                    const promises = [];

                    const gameTitle = req.body.gametitle
                    const targetGID = req.body.target
                    const numPlayers = req.body.playercount.toString()

                    const newGID = newGidToString

                    const collections = ['games', 'clues', 'detours', 'messages', 'questions', 'tagstate']
                    collections.forEach(function (element) {

                        console.log('start loop', element)

                        let elementsRef = admin.firestore().collection(element)
                        elementsRef = elementsRef.where('gid', '==', targetGID)
                        // elementsRef.get().then(snapshot => {

                        const promGet = elementsRef.get();  //lets get our promise
                        promises.push(promGet);             //place into an array
                            promGet.then(snapshot => {  //we can now continue as before

                            var batch = admin.firestore().batch()

                            snapshot.forEach(function (doc) {
                                // change the gid to the new game gid
                                let data = doc.data()
                                data.gid = newGID

                                if(data.template){
                                    data.template = false
                                }

                                if(!data.parent) {
                                    data.parent = targetGID
                                }

                                if (!data.playercount) {
                                    data.playercount = numPlayers
                                }

                                if (data.gametitle) {
                                    data.gametitle = gameTitle
                                }

                                let elementRef = admin.firestore().collection(element).doc()
                                batch.set(elementRef, data)
                            })
                            batch.commit().then(data => {
                                console.log('complete batch ', element)
                            })

                        })
                    })
                    Promise.all(promises).then(() => {
                        res.send({ status: 200, message: 'Game: added.', pin: newGID })
                        console.log('completed');
                    });
                    // res.send({ status: 200, message: 'Game: added.', pin: newGID})
                    // console.log('completed');
                } else {
                    console.log('found a match ', snapshot)
                    // re-run the generator for a new pin
                    generatePin()
                }
            })
        }

SECOND UPDATE: (based off Keith's answer)

function addGame(newGidToString) {
            var getGame = admin.firestore().collection('games')
            getGame = getGame.where('gid', '==', newGidToString)
            getGame.get().then(snapshot => {
                if (snapshot.empty) {

                    console.log('BODY ', req.body)

                    const gameTitle = req.body.gametitle
                    const targetGID = req.body.target
                    const numPlayers = req.body.playercount.toString()

                    const newGID = newGidToString

                    const promises = [];

                    const collections = ['games', 'clues', 'detours', 'messages', 'questions', 'tagstate']
                    collections.forEach(function (element) {

                        console.log('start loop', element)

                        let elementsRef = admin.firestore().collection(element)
                        elementsRef = elementsRef.where('gid', '==', targetGID)

                        const getPromise = elementsRef.get();  //lets get our promise
                        promises.push(getPromise);             //place into an array
                        getPromise.then(snapshot => {  //we can now continue as before

                            var batch = admin.firestore().batch()

                            snapshot.forEach(function (doc) {
                                // change the gid to the new game gid
                                let data = doc.data()
                                data.gid = newGID

                                if(data.template){
                                    data.template = false
                                }

                                if(!data.parent) {
                                    data.parent = targetGID
                                }

                                if (!data.playercount) {
                                    data.playercount = numPlayers
                                }

                                if (data.gametitle) {
                                    data.gametitle = gameTitle
                                }

                                let elementRef = admin.firestore().collection(element).doc()
                                batch.set(elementRef, data)
                            })
                            batch.commit()
                        })
                    })
                    Promise.all(promises).then(() => {
                        res.send({ status: 200, message: 'Game: added.', pin: newGID })
                        console.log('completed from promise');
                    });
                } else {
                    console.log('found a match ', snapshot)
                    // re-run the generator for a new pin
                    generatePin()
                }
            })
        }

回答1:


When looping with promises, one thing to remember is if you do a forEach, it's not going to wait.

It appears that what the OP wants to do is before he hits the complete, and does a res send, wants to make sure all promises have been completed.

Promise.all https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all , is exactly what this does.

So every time you create a promise if you push this onto an array, we can later use Promise.all to make sure all the promises are complete.

So if we replace

elementsRef.get().then(snapshot => {

with

const promGet = elementsRef.get();  //lets get our promise
promises.push(promGet);             //place into an array
promGet.then(snapshot => {          //we can now continue as before

Your promise is now stored for later so that Promise.all can check to make sure everything has complete. You could do this in one line, but I feel it's easier to see what's happening this way. eg->

promises.push(promGet.then(snapshot => {

Finally now we have got our promise array, before we do our res.send, lets make sure they have all completed.

Promise.all(promises).then(() => {
  res.send({ status: 200, message: 'Game: added.', pin: newGID})
  console.log('completed');
});

One thing to keep in mind when using Promises this way, is that you have a fair few things going on at the same time, sometimes this can be an issue, eg. connected to some service might put restrictions on the number of concurrent connections etc. So in these cases it might be an idea doing things in series or a mix of both. There are even libs that can do concurrecy, eg. Do only X amounts of promises at a time.

And finally, if you can use async / await, I'd advice using them, as it makes Promises so much nicer to work with.



来源:https://stackoverflow.com/questions/55972558/having-a-promise-issue-with-my-google-cloud-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!