Prevent consecutive clicks of Google Sheets Image Buttons in with Lock Service and Google Apps Script

Prevent consecutive clicks of a Google Sheet Image Button with Lock Service

Ever seen an overzealous user madly clicking on an image button in Google Sheets to run an automation script? It’s maddening, frustrating and most importantly, may cause errors in your beautifully crafted code.

In this short tutorial, we explore how to use Google Apps Script’s Lock Service to prevent users from executing your code from your image button while the original instance of the code is still running.

Grab a copy of the starter sheet below to play along.

To the Starter Sheet

 

The  Video Tutorial

Setup

In our starter sheet, we have a basic button that, when clicked:

  1. Retrieves the current count from the cache using Cache Service.
  2. Generates a toast (That’s a small message box down the bottom right of the sheet) when the counter starts at zero.
  3. Displays the count from the counter in cell C16.
  4. Waits for 10 seconds to simulate a whole bunch of very awesome code going on in the background before updating the counter by one.
  5. Stores the count back in the cache for 15 seconds.

Here’s the code:

Go ahead and add the code to your starter sheet and go crazy clicking the button. What’s occurring?

Yeap, the script tries to run each time you click the button, but the cache hasn’t been updated yet to store the new values.

This would simulate a user attempting to run a process multiple times but the process has yet to complete. However, it relies on the result of the previous process to accurately run the next one. Not ideal.

Another scenario may be that you want to minimise unnecessary clicks. Imagine that when the user clicks a button, the script appends a new piece of data to a Google Sheet. Perhaps they only wanted to click the button once but they got a little click-happy (we’ve all been there) and did a double click instead.

You can simulate this in the code above by moving the Utilities.sleep() method to below the cacheServ.put method.

Go ahead and give it a try. You will see the counter increasing but a bunch of executions stack on top of each other on the Sheet UI.

Move the sleep method back to above the couner++ line.

Let’s dive into a solution.

Hire a Google Workspace Developer for your Business Needs

Limiting consecutive executions with Lock Service

The Google Apps Script Class Lock Service allows us to prevent simultaneous access to certain parts of our code.

Lock Service allows us to lock a part of a script under 3 circumstances:

  1. Document Lock (getDocumentLock): This prevents a user of the current document from running the desired section of code when the code is already being run.
  2. Script Lock (getScriptLock): This prevents all users from running the desired section of code when the code is already being run.
  3. User Lock (getUserLock): This prevents the same user from running the desired section of code when they are already running that code.

For our example, we could have selected User Lock in the circumstance where we did not want them to madly click the image button, but it wouldn’t have saved us from other users who may also be trying to execute the code from the same button.

We could, however, have used the document lock or the script lock in our example as we are running the code within the one document. Let’s settle on Script Lock.

Building the Lock on the script

Get the lock class

First, we call the Google Apps Script Lock Service  requesting the scriptLock() method.

  const lock = LockService.getScriptLock()
We will add this just below the SpreadsheetsApp request for the current sheet because we will use the sheet to generate a ‘toast’ to notify us the script is already running.
The lock variable does not immediately create the lock on the script. Rather, it provides access to the lock class.

Try Lock or Wait lock?

The region that will be locked will start when one of the following lock methods is called:
  • Try Lock: tryLock(timeoutInMillis) – When the user executes the code, the script will make several attempts to run until the timeout period has expired. The method will return a boolean, true if it can successfully run the code or a false if it cannot.
    This is useful if you want to carry out some other process if the current one is being occupied and the lock period expires. Say the lock period expires and you save the data inputs in a cache and update it later when the process is not so busy.
    Alternatively, you can call a return on the function and gracefully complete the execution of the script.
  • Wait Lock: wailtLock(timeoutInMillis) – This method differs from Try Lock in that rather than returning a boolean response, it throws an error if the timeout period is exceeded and the script is not freed up to be run.

We don’t want to freak our users out with an error, so we are going to use Try Lock in our example.

🐐You can support me for free by using this Amazon affiliate link in your next tech purchase :Computers & Stuff! 🐐

So the next question we need  to ask is, ‘How long do we want to set our time out?’

Well, in the scenario where we just don’t want to process overenthusiastic clicks, then we should set the timeout to zero milliseconds.

On the other hand, if we are trying to wait for a process to conclude first then we might want to just delay the lock attempt for a little over the average time it takes to run the script.

In the end, being an image button on a Google Sheet, it is probably best to just set the Try Lock to zero, add a warning that there is an execution already underway and have them attempt the button again a little later.

Below out last lock code add the following:

const success = lock.tryLock(0)

If the code is running, warn the user and don’t run the code

We can now check our success variable to see if it is false. If it is, then we want to send a toast to the user’s UI and complete the script. We can do this by returning a Spreadsheet App toast() message. This method can take a single string as an argument, our warning message.

Add the following code below the success variable.

if (!success) return ss.toast("locked!")
Now any code after this message will be locked from other users while the script is being run.
Check out the updated code:

 

Give it a run.

Ending the lock

The lock will end naturally when the script execution is complete.

However, if you want a concurrent user  to run the script outside of the locked region use can use the releaseLock() method.

We will set the lock to encompass the rest of the code. So at the bottom of our function, we will add the following:

lock.releaseLock()
What would happen if we didn’t do this? Well in our circumstance, nothing.
However, if we did not have our if > success statement then any code after the released lock would run if the try lock method returned false.
To test this out lets move the to below the if > success release lock section.
if (!success) return ss.toast("locked!")

Run the code

Go ahead and run the code from the button in the UI. Notice the “locked!” toast message appears down the bottom right of the screen after consecutive clicks while the initial script is still running.

Our number only updates after the previous execution is complete.

You will also see that the script executions complete faster providing less of a script backlog.

 

Create and Publish Google Workspace Add-ons with Apps Script Course 300px

I would love to hear how you would use this in your own project. Let me know in the comments below.

If you want to see lock service in a larger project using a WebApp and Google Sheets, check out this tutorial:

Creating an embedded interactive Chain Story app with Google Apps Script and Google Sheets

 

You can also learn how to remove a Google Sheets button after it has been clicked here.

Create and Publish a Google Workspace Add-on with Apps Script Course

Need help with Google Workspace development?

Go something to solve bigger than Chat GPT?

I can help you with all of your Google Workspace development needs, from custom app development to integrations and security. I have a proven track record of success in helping businesses of all sizes get the most out of Google Workspace.

Schedule a free consultation today to discuss your needs and get started or learn more about our services here.

The Full Code with Google Apps Script Lock Service


 

~ Yagi

2 thoughts on “Prevent consecutive clicks of Google Sheets Image Buttons in with Lock Service and Google Apps Script”

  1. Nice article design and good topic. But probably better can be more simply structure (without additional cache service) and some explaining how set/test works. Had to make own testing to understand.

    *edit spelling ‘cash’ to ‘cache’

    1. I am curious how you would avoid something like CacheService when each click of the button is its own execution?

      Great to hear you generated your own tests. Best way to learn. This is definitely not a website for script kiddies 😉 .

      ~ Yagi.

Leave a Reply