Avoiding GCF anti-patterns part 4: How to handle Promises correctly in your Node.js Cloud Function
Senior Developer Relations Engineer
Technical Solutions Engineer
Try Google Cloud
Start building on Google Cloud with $300 in free credits and 20+ always free products.Free trial
Editor's note: Over the next several weeks, you'll see a series of blog posts focusing on best practices for writing Google Cloud Functions based on common questions or misconceptions as seen by the Support team. We refer to these as "anti-patterns" and offer you ways to avoid them. This article is the fourth post in the series.
You notice that the data your Function saves to a database is either "undefined" or it is saving a cached value. For example, you have a Cloud Task that every hour invokes a Cloud Function that retrieves data from one database, transforms that data, and then saves the modified data to another database. Yet, you notice that your data is either undefined or a cached value.
Most common root issue
An unhandled promise.
One common anti-pattern we notice when using the then-able approach in a Cloud Function is that it is easy to overlook an unhandled Promise. For example, can you spot the issue in the following example?
You will not know how long the call
transformData(response.data) will take. And more likely, this call is probably an async method. The result is that the
saveDataToDatabase method is executed before
() has completed. Thus, the variable
dataNowTransformed is undefined upon saving to the database.
How to investigate
- Are you using
await? If not, we recommend using async and await keywords to help improve the readability of your code.
- If you cannot convert to await at this time, you'll need to add a logging layer (see example below) to determine if you have an unhandled promise.
How to add a logging layer using then-able functions
There are two ways to log:
- sync logging by modifying your callbacks
- async logging to avoid modifying your callbacks (i.e. adding a logging layer using then-able functions)
Suppose you are okay with modifying your callbacks. You can add a synchronous logging layer just by using
console.log(). We recommend creating a synchronous method called
() function to keep your code clean and avoid numerous
() statements throughout your code.
logData() looks like:
Now suppose you do not want to modify the code within your callbacks. We recommend adding an async logging method as follows:
1. Create an async
logDataAsync() method in your Function.
2. Call the
logDataAsync method using the then-able() approach
Please see the helpful tips section for a more compact way to apply the async logging approach.
How to handle the Promise using the then-able approach
We recommend that you perform one task at a time within a
.then() callback. Going back to our original anti-pattern example, let's update it to use the then-able approach. Here's an end-to-end working example:
But all these back to back
.then() calls (called Promise chaining) make the code difficult to read and maintain. Please see the helpful tips section for a more compact way to write this code, in case you see it elsewhere.
If possible, we suggest that you use awaits. Notice how the code in the Function event handler
callToSlowRespondingAPI now becomes more succinct and readable. In addition, if anything goes wrong in these async method calls, an exception is thrown, in lieu of returning null or false in the return statement.
Other helpful tips
- Are you testing locally using the Functions Framework? You can follow this codelab to learn how to debug Node.js functions locally in Visual Studio Code.
- Whenever you are logging data, be aware of how much data you are logging (see log size limits) and whether your data has any sensitive information or personally identifiable information.
- Using the then-able() approach, you might often see code written as follows. This is functionally equivalent to the longer then-able() Promise-chaining version used above. However, we recommend using the async await approach for readability.
Avoiding GCF anti-patterns part 3: How to establish outbound connections correctly
Third post in a series on how to avoid anti-patterns in Google Cloud Functions as seen by the Support team. This post explores how to make outbound connections correctly by using explicit timeouts on your outbound calls.
By Sara Ford • 3-minute read