Google Apps Script web-app not running asynchronously as expected

Viewed 135

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...

1 Answers

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>

Related