Reputation: 542
I am relatively new to the async world of Javascript. My function makes some calls to the Firebase Admin SDK and also some fetch requests to a third party API. The function works, but every once in a while I get a socket hang up
error. I essentially want to wait until all the await functions complete before sending res.end()
, but it looks like I am doing something wrong in terms of my use of async/await
.
My function finishes (looks like it hits res.end()
) but it keeps continuing on:
And then strangely enough, an error from the same execution ID shows up in the following execution ID:
Here is how I am structuring my code:
exports.myFunction = async (req, res) => {
// parse the body...
// if it's a closed order
if (json.hasOwnProperty('closed_at')) {
// get order object from orders directory
await ordersRef.once("value", async function(snapshot) {
var orderResponseObj = snapshot.val();
// do some stuff with orderResponseObj
// ** res.end() CALLED BEFORE THIS HAPPENS **
await deleteRef.once("value", async function(snapshot) {
var deleteResponseObj = snapshot.val();
// do some stuff with the delete object
});
// get object from shop directory
await shopInfoRef.once("value", async function(snapshot) {
var firebaseResponseObj = snapshot.val();
await updateInventory.updateInventory(); // do some fetch request to 3rd party API
await deleteProduct.deleteProduct(); // do another fetch call to 3rd party API
});
});
}
// terminate the cloud function
res.end();
}
Upvotes: 3
Views: 2238
Reputation: 1878
So you have some nested promises that will not actually wait all of them to be completed, your structure is like this:
| 1 exports.myFunction
| | 1 ordersRef.once
| | | 1 deleteRef.once
| | | 2 shopInfoRef.once
| | | | 1 updateInventory.updateInventory
| | | | 2 deleteProduct.deleteProduct
The fact is that your async functions will only await for the first lower level promises, so for example deleteRef.once
will just await for shopInfoRef.once
, but not for updateInventory.updateInventory
. This means that your top level exports.myFunction
will wait only that ordersRef.once
resolves, ignoring the rest. The numbers indicate the order of the execution of the promises, since you are using await for all of them, we have no promise on the same level firing togheter (no duplicated numbers).
Now in order to wait for the end of all the function cycles you can implement your own chain of promises. Looking at your code, the very last promise to await is the deleteProduct.deleteProduct
, because when you reach that point, any other promise is resolved, because of all the await
keywords.
exports.myFunction = async (req, res) => {
// parse the body...
// if it's a closed order
if (json.hasOwnProperty('closed_at')) {
let awaiter = new Promise((resolve) => {
// get order object from orders directory
await ordersRef.once("value", async function(snapshot) {
var orderResponseObj = snapshot.val();
// do some stuff with orderResponseObj
// ** res.end() CALLED BEFORE THIS HAPPENS **
await deleteRef.once("value", async function(snapshot) {
var deleteResponseObj = snapshot.val();
// do some stuff with the delete object
});
// get object from shop directory
await shopInfoRef.once("value", async function(snapshot) {
var firebaseResponseObj = snapshot.val();
await updateInventory.updateInventory(); // do some fetch request to 3rd party API
await deleteProduct.deleteProduct(); // do another fetch call to 3rd party API
resolve(); // we put resolve in the very last point of the chain
});
});
});
// await make the execution of your async function
// wait until the "resolve" function of our "awaiter"
// promise is called, so at the very last of the chain
await awaiter;
}
// terminate the cloud function
res.end();
}
Obviously you can contract the code in a form like await new Promise(resolve => { ... })
, but I divided the two sentences just for sake of clarity.
The new structure results like this:
| 1 exports.myFunction
| | 1 awaiter
| | - 1 ordersRef.once
| | - | 1 deleteRef.once
| | - | 2 shopInfoRef.once
| | - | | 1 updateInventory.updateInventory
| | - | | 2 deleteProduct.deleteProduct
| | ----------> 3 awaiter.resolve
Upvotes: 4