Skip to content

How to Implement LockService in Google App Script The Right Way?

Currently, I’m trying lockservice in Google App Script to handle concurrency on my webapp. However, when I implement it, it did not work.

Description of my webapp: enter image description here

Whenever a user click on the button, it will get the website address value in the google sheet and open a new window to go to the website. Then, it will update the value in my google sheet with the value of a new website address.

on this webapp, it will cycle through 3 websites Google -> Youtube -> Facebook

The problem:

When I tried testing it for concurrency with clicking the button fast, it will return the same website. For example, if I clicked it twice, it will open 2 new window of Google, but the value in the google sheet will be updated to Facebook, which skip the Youtube.

I tried implement lockservice but it did not work, maybe I implement it wrongly.

this is my HTML code:

    <html>
  <head>
    <base target="_top">
  </head>
  <body>
    <div class="content">
    <h1>Please Click Below</h1>
    <!-- <h2><?=web?>:</h2> -->
    <button class="button" id = "btn" type="submit" >Open Window</button>
    </div>        
  <script>
  document.getElementById("btn").addEventListener("click", doStuff);
  var web = "";

  function doStuff() {
        google.script.run.withSuccessHandler(openWebsite).getWebsite();
      }

      function openWebsite(web) {
        if (web === "Google") {
          window.open("https://www.google.com/");
        } else if (web === "Youtube") {
          window.open("https://www.youtube.com/");
        } else {
          window.open("https://www.facebook.com/");
        }
        google.script.run.setWebsite();
      }
</script>

  </body>
</html>

My code.gs:

    var url = "google sheet url";
var web = "";

function doGet(e) {
 
  let tmp = HtmlService.createTemplateFromFile("index");
  let output = tmp.evaluate().addMetaTag('viewport', 'width=device-width, initial-scale=1');
  return output;

}

function setWebsite () {
  var ss = SpreadsheetApp.openByUrl(url);
  var ws = ss.getSheetByName("website");
  web = ws.getRange(1,1,1,1).getValue();
  var lock = LockService.getScriptLock(); //del
  lock.tryLock(5000); //del
  if (lock.hasLock()) { //del
    if (web === "Google") {
      ws.getRange(1,1,1,1).setValue("Youtube");
    } 
      else if (web === "Youtube") {
      ws.getRange(1,1,1,1).setValue("Facebook");
    }
      else {
      ws.getRange(1,1,1,1).setValue("Google");
    } 

    var xs = ss.getSheetByName("data");
    xs.appendRow([new Date(), web]);
    lock.releaseLock(); //del
    }
  } //del

function getWebsite() {  
  var lock = LockService.getScriptLock();
  lock.tryLock(5000); // wait 5 seconds try to get lock 

  if (lock.hasLock()) {
  var ss = SpreadsheetApp.openByUrl(url);
    var ws = ss.getSheetByName("website");
    var web = ws.getRange(1,1,1,1).getValue();
    lock.releaseLock();
    return web;
    }
  }

// function getWebsite() {  
//   var lock = LockService.getScriptLock();
//   try {
//     lock.waitLock(5000); // wait 5 seconds try to get lock
//   } catch (e) {
//     Logger.log('Could not obtain lock after 5 seconds.');
//   }
//     Utilities.sleep(1000);
//     var ss = SpreadsheetApp.openByUrl(url);
//     var ws = ss.getSheetByName("website");
//     var web = ws.getRange(1,1,1,1).getValue();
//     lock.releaseLock();
//     return web;
//     //lock.releaseLock();
//     }

ps. I tried two different ways (on the comment part in code.gs) that I can find on Stackoverflow and youtube but still no success.

Please help. Thank you.

Answer

I thought that in your script, when the button is clicked, google.script.run.withSuccessHandler(openWebsite).getWebsite() is run and also google.script.run.setWebsite() is run in the function openWebsite. In this case, the LockService is used for each function of getWebsite() and setWebsite(). I thought that this might be the reason of your issue. So, in your script, how about the following modification? In this modification, one LockService is used for the functions of getWebsite() and setWebsite().

HTML&Javascript side:

Please modify openWebsite(web) in your Javascript side as follows.

From:

google.script.run.setWebsite();

To:

// google.script.run.setWebsite();
  • In this modification, google.script.run.setWebsite(); is removed.

Google Apps Script side:

Please modify getWebsite() in your Google Apps Script side as follows.

From:

var web = ws.getRange(1,1,1,1).getValue();
lock.releaseLock();

To:

var web = ws.getRange(1, 1, 1, 1).getValue();
setWebsite(); // Added
lock.releaseLock();
  • In this modification, google.script.run.setWebsite(); is removed.

Note: