Skip to content
Advertisement

Realtime database cloud function sends result back before finishing

I’m trying to perform some realtime database tasks, which after completion, should send back a result back to the client so the client knows when the tasks have finished.

exports.createGame = functions.https.onCall((data, context) => {
    const gameChosen = data.gameChosen; 
    const adminName = data.adminName;
    const numberOfRounds = data.numberOfRounds;
    let gameID = Math.floor(100000 + Math.random() * 900000).toString();
    var numberOfQuestions = 0;
    var questionsPicked = [];

    getGameQuestions(gameChosen, numberOfRounds).then((questionsPickedArray) => {
        db.ref(`live-games/${gameChosen}/${gameID}`).set(
         {
           playerAdmin: adminName,
           game: gameChosen,
           players: [adminName],
           questions: questionsPickedArray,
           datetimeCreated: Date.now(),
         },
          (error) => {
            if (error) {
              console.log("Data could not be saved." + error);
            } else {
              console.log("Data saved successfully.");
              return {
                gameChosen: gameChosen,
                gameAdmin: adminName,
                questions: questionsPicked,
                gameID: gameID,
              };
             }
           } 
        );
      });
    });

function getGameQuestions(gameChosen, numberOfRounds) {
  var questionsPicked = [];
  var numberOfQuestions = 0;
  return new Promise(function (resolve, reject) {
    db.ref(`games/${gameChosen}`).once("value", function (snapshot) {
      val = snapshot.val();
      numberOfQuestions = val.questionsAvailable;
      for (var i = 0; i < numberOfRounds; i++) {
        let questionNumber = Math.floor(Math.random() * (numberOfQuestions - 0 + 1) + 0);
        if (!questionsPicked.includes(questionNumber)) {
          questionsPicked.push(questionNumber);
        }
      }
      resolve(questionsPicked);
    });
  });
}

I’ve tried to create a promise due to the fact some realtime database tasks do not return a promise – wasn’t sure which one. Based on the logs, the function completed with a status code of 200 and then a couple seconds after, the realtime database gets updated with the values. The database should be updated, the result sent back to the client and then the function should finish. It’s currently sending back NULL to the client – presuming the function is sending it back as soon as it runs.

How does one perform realtime database tasks one after another efficiently?

Advertisement

Answer

The following modifications should do the trick:

exports.createGame = functions.https.onCall((data, context) => {
    const gameChosen = data.gameChosen;
    const adminName = data.adminName;
    const numberOfRounds = data.numberOfRounds;
    let gameID = Math.floor(100000 + Math.random() * 900000).toString();
    var numberOfQuestions = 0;
    var questionsPicked = [];
 
    return getGameQuestions(gameChosen, numberOfRounds)  // We return the Promises chain, see below for more details
        .then((questionsPickedArray) => {
            return db.ref(`live-games/${gameChosen}/${gameID}`).set(
                {
                    playerAdmin: adminName,
                    game: gameChosen,
                    players: [adminName],
                    questions: questionsPickedArray,
                    datetimeCreated: Date.now(),
                })
        })
        .then(() => {
            return {
                gameChosen: gameChosen,
                gameAdmin: adminName,
                questions: questionsPicked,
                gameID: gameID,
            };
        })
        .catch((error) => {
            // See https://firebase.google.com/docs/functions/callable#handle_errors
            throw new functions.https.HttpsError(...);
        });
});

function getGameQuestions(gameChosen, numberOfRounds) {
    var questionsPicked = [];
    var numberOfQuestions = 0;
        return db.ref(`games/${gameChosen}`).once("value")  // Here too, we return the Promises chain
        .then(snapshot => {
            val = snapshot.val();
            numberOfQuestions = val.questionsAvailable;
            for (var i = 0; i < numberOfRounds; i++) {
                let questionNumber = Math.floor(Math.random() * (numberOfQuestions - 0 + 1) + 0);
                if (!questionsPicked.includes(questionNumber)) {
                    questionsPicked.push(questionNumber);
                }
            }
            return questionsPicked;
        });
}

So, you need to chain the Promises and return the result to the client as shown above: as explained in the doc “the data returned by the promise is sent back to the client” (which is the case if we return the Promises chain) and the data shall be an object/value that can be JSON encoded (which is the case with the { gameChosen: gameChosen, gameAdmin: adminName, ...} object).

For the getGameQuestions() function, since the once() method returns a Promise, you don’t need to encapsulate it into a Promise. Again, just return the promises chain composed by once().then(...).

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement