Hsu Sean
Hsu Sean

Reputation: 33

How to call google apps script server-side functions synchronously?

I wrote a google apps script code, it will open a google spread sheet, and list the values by row, but there are 2 problems: 1. The output by random order. 2. The div text which id "loding" change to "Finished!" before list all of values. I thought the script will wait for server-side function return when I run it by "withSuccessHandler()", but it's not. How can I correct it?

index.html:

<!DOCTYPE html>
<html>
    <head>
        <base target="_top">
        <script>
            function    jsListValue() {
                // Get count.
                google.script.run.withSuccessHandler(function(count) {
                    // List all values.
                    for( count; count>0; count=count-1) {
                        // Get a value.
                        google.script.run.withSuccessHandler(function(content) {
                            // Shows in "output".
                            var new_div = document.createElement("div");
                            new_div.appendChild(document.createTextNode(content));
                            document.getElementById("output").appendChild(new_div);
                        }).gsGetValue(count);
                    }
                    // Change loding notice.
                    document.getElementById("loding").innerHTML = "Finished!";
                }).gsGetCount();
            }
        </script>
    </head>
    <body onload="jsListValue()">
        <div id="output"></div>
        <div id="loding">Loding now...</div>
    </body>
</html>

code.gs

function                doGet() {
    return HtmlService.createHtmlOutputFromFile('index').setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function                gsOpenSheet() {
    // Return sheet of the note data.
    return (SpreadsheetApp.openById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").getSheetByName("sheet1"));
}
function                gsGetCount() {
    // Return last row index in this sheet.
    return (gsOpenSheet().getLastRow());
}
function                gsGetValue(index) {
    // Return value in the (index,1).
    return (gsOpenSheet().getRange(index,1).getValue());
}

Upvotes: 3

Views: 3072

Answers (4)

user2948287
user2948287

Reputation: 1

As hinted by @Iuri Pereira If you return a parameter from the google script code, and then use it in the page javascript, then the procedure runs synchronously.

HTML:

<button onclick="google.script.run.withSuccessHandler(js_function).gs_code();">action</button>

GS:

function gs_code() {
   // do Something;
   return true;
}

HTML javascript:

function js_function(gs_return_value) {
   console.log(gs_return_value);
}

Upvotes: 0

Iuri Pereira
Iuri Pereira

Reputation: 16

google.script.run is asynchronous, which means that is impossible to predict in what order gsGetValue(count) will return. When you're using withSuccessHandler you must perform the next action inside the callback function.

My suggestion is to get all the range you want and put it on an array. You can create a serverside function to do this. The code would look like this:

//Serverside function
function getDataForSearch() {
  const ws = SpreadsheetApp.openById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").getSheetByName("sheet1");

  return ws.getRange(1,1,ws.getLastRow(),1).getValues();
}

getValues() will return an array of arrays that should contain all values from the range specified. More information about here

On your client-side, the script should be like this:


//array to get the data from getRange()
var data;

function jsListValue() {
   google.script.run.withSuccessHandler(function(dataReturned){
      data = dataReturned;
      var new_div;
      data.forEach(function(r){
         new_div = document.createElement("div");
         new_div.appendChild(document.createTextNode(r[0));
         document.getElementById("output").appendChild(new_div);
      });
      document.getElementById("loding").innerHTML = "Finished!";
   }).getDataForSearch();
}                        

Upvotes: 0

You want to use withSuccessHandler Docs

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onSuccess(numUnread) {
        var div = document.getElementById('output');
        div.innerHTML = 'You have ' + numUnread
            + ' unread messages in your Gmail inbox.';
      }

      google.script.run.withSuccessHandler(onSuccess)
          .getUnreadEmails();
    </script>
  </head>
  <body>
    <div id="output"></div>
  </body>
</html>

Upvotes: 2

Sujay Phadke
Sujay Phadke

Reputation: 2196

GAS is very similar to Javascript, and all calls to Google's server side functions are asynchronous. You cannot change this (at least I haven't seen any doc reg. that).

What you can do, is, use a callback function on the client side which polls the server for a "success" return value. It'll keep polling it say for 1 minute, or else exit. Let it set a client flag to "true" if the success value is returned by the server. Nothing should proceed on the client side, unless the flag is true. In this way, you can control what happens on the client side.

Upvotes: 4

Related Questions