2

I've got a non-blocking (i.e. no LockService) web-app that returns a counter stored in ScriptProperties.

I have an artificial sleep in the script to test asynchronous calls to the web-app.

Looking at this code below, I would assume that two successive calls to the web-app should return the same number.

However, it does not. Two calls to the web-app, one right after the other, returns increasing numbers. This makes me think the first call finishes before the second one runs -- which doesn't make sense.

function doGet(e)
{
    // get script properties
    var scriptProperties = PropertiesService.getScriptProperties();
    
    // get ID
    var id = parseInt(scriptProperties.getProperty("id"));

    // fake a long process
    // enough time to make another call to the web-app
    // in theory, the second call will get the same value for `id`
    Utilities.sleep(5000);

    // write a new value
    scriptProperties.setProperty("id", id + 1);
    
    // return it
    return ContentService.createTextOutput(id);
}

I am trying to figure out how/why. Does Google not support asynchronous calls for web-apps?

You can see this running at https://script.google.com/macros/s/AKfycbxP6TQeMv_4b1lsYvGLA3YAn_reBhZ64Y2d04DotQ4CFJQtKhM/exec.

** UPDATE **

Here is a local HTML file I am using to test.

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script type="text/javascript">
        function doIt()
        {
            console.log("doIt: start");
            setTimeout(callIt, 500);
            console.log("doIt: end");
        }

        function callIt()
        {
            console.log("callIt: start");
            var request = new XMLHttpRequest();
            request.open('GET', 'https://script.google.com/macros/s/AKfycbxP6TQeMv_4b1lsYvGLA3YAn_reBhZ64Y2d04DotQ4CFJQtKhM/exec', true);

            request.onload = function()
            {
                if(this.status >= 200 && this.status < 400)
                {
                    document.querySelector("#output").innerText += this.response + ", ";
                }
                else
                {
                    alert("error");
                }
            };

            request.onerror = function()
            {
                alert("error");
            };

            request.send();
            console.log("callIt: end");
        }
    </script>
  </head>
  <body>
    output:
    <div id="output"></div>
    <input type="button" value="Click me" onclick="doIt()">
  </body>
</html>

Clicking the button very quickly should return the same number, but it does not...

3
  • Can you provide the script and/or your method for requesting to the Web Apps? Because when the script of UrlFetchApp.fetchAll([{url: url}, {url: url}]) is used, 2 same values are returned. Commented Sep 27, 2020 at 22:33
  • I just opened script.google.com/macros/s/… in two tabs very quickly. I even tried a local HTML file on my computer. I'll update the question with its soruce. Commented Sep 27, 2020 at 22:40
  • 2
    Thank you for replying. From Does Google not support asynchronous calls for web-apps? in your question and your additional script, I thought that your question might have 2 questions. So at first, about Does Google not support asynchronous calls for web-apps? in your question, how about confirming this? In order to check it, as the simple script, you can do it using the Google Apps Script of UrlFetchApp.fetchAll([{url: url}, {url: url}]).forEach(e => console.log(e.getContentText()));. As the next step, your script is considered. How about this? Commented Sep 27, 2020 at 22:47

1 Answer 1

4

Yes! Web Applications calls like doPost() run asynchrounously.

Issue:

The reason you were not able to replicate collision is because your sleep time was too low relative to the time taken by the browser to make http calls

Solution:

  • Increase sleep to 1 minute or more.

    Utilities.sleep(1*60*1000);
    
  • You can also easily prove asynchronicity, if you use a random sleep time with the below snippet script, which will show each loop's time in addition to random "callIt end"s.

    Utilities.sleep(Math.floor(Math.random()*60000)+1);
    

/*<ignore>*/console.config({maximize:true,timeStamps:false,autoScroll:true});/*</ignore>*/
function callIt(i) {
  const label = 'callIt' + i;
  console.log(label + ' start');
  console.time(label);
  const request = new XMLHttpRequest();
  request.open(
    'GET',
    'https://script.google.com/macros/s/AKfycbxP6TQeMv_4b1lsYvGLA3YAn_reBhZ64Y2d04DotQ4CFJQtKhM/exec',
    true
  );

  request.onload = function() {
    if (this.status >= 200 && this.status < 400) {
      console.log(this.response + `- ${label}`);
      console.log(label + ' end');
      console.timeEnd(label);
      console.log('\n')
    } else {
      alert('error');
    }
  };

  request.onerror = function() {
    alert('error');
  };

  request.send();
}
let i = 0;
while (++i < 10) callIt(i);
<!-- https://meta.stackoverflow.com/a/375985/ -->    <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.