3
votes

I'm using Firestore, Cloud Functions and Dialogflow to create a Google Home app. My cloud function should return a calculation based on values in a collection, but I can only return a Promise.

// query firestore based on user
var transactions = db.collection('accounts')
               .doc(request.body.result.parameters.accountowner)
               .collection('transactions');
var accountbalance = await transactions.get()
    .then(snapshot => {
        var workingbalance = 0
        snapshot.forEach(doc => {
            workingbalance = workingbalance + doc.data().amount;
        });
        return workingbalance
    })
    .catch(err => {
        console.log('Error getting transactions', err);
    });

console.log(accountbalance)
balanceresponse = {
    "speech": request.body.result.parameters.accountowner + " has a balance of $" + accountbalance,
    "displayText": request.body.result.parameters.accountowner + " has an account balance of $" + accountbalance,
    "data": {},
    "contextOut": [],
    "source": "system"
}

response.send(balanceresponse);    

How can I wait for the promise to return the actual value before calling response.send? I have also tried to put the response.send inside a then, but the function completes and sends headers before the operation completes.

1
Just to be clear - you're using await here? Are you using an additional library to add this? (GCF runs Node 6.11, which doesn't have await natively, I thought.)Prisoner
Here is a more complete snippet gist.github.com/anonymous/7982d7771237f10031102a0281a38b47 (I have removed await)Daniel Watrous

1 Answers

1
votes

If you're working with promises, you need to return values as part of the Promise.then chain. This is usually done by chaining a bunch of .then() calls/blocks together. Remember, the then() portion will only be called when a Promise resolves.

In your case, it looks like you can just generate balanceresponse and call the response.send() after you calculate workingbalance, inside the same block of code. The call to response.send() doesn't need to be at the end of the function. So you might have that block look something like:

.then(snapshot => {
    var workingbalance = 0
    snapshot.forEach(doc => {
        workingbalance = workingbalance + doc.data().amount;
    });

    balanceresponse = {
      "speech": request.body.result.parameters.accountowner + " has a balance of $" + workingbalance,
      "displayText": request.body.result.parameters.accountowner + " has an account balance of $" + workingbalance,
      "data": {},
      "contextOut": [],
      "source": "system"
    }

    response.send(balanceresponse); 
})

If, for some reason, you wanted to separate the calculation and compute the balanceresponse in a different block, you could do so. I don't necessarily recommend this, but it has some merits (for example, you could put multiple catch blocks after each part, which I've shown here):

// query firestore based on user
var transactions = db.collection('accounts')
               .doc(request.body.result.parameters.accountowner)
               .collection('transactions');
await transactions.get()

    .then(snapshot => {
        var workingbalance = 0
        snapshot.forEach(doc => {
            workingbalance = workingbalance + doc.data().amount;
        });
        return workingbalance
    })

    .catch(err => {
        console.log('Error getting transactions', err);
    });

    .then( accountbalance => {
        balanceresponse = {
            "speech": request.body.result.parameters.accountowner + " has a balance of $" + accountbalance,
            "displayText": request.body.result.parameters.accountowner + " has an account balance of $" + accountbalance,
            "data": {},
            "contextOut": [],
            "source": "system"
        }
        response.send(balanceresponse);    
    })

    .catch(err => {
        console.log('Error sending response', err);
    });