2

I need the client side code to wait for the called server side (google.script.run) function to complete before running any more code.

The withSuccessHandler(successFunc) does not cause lines of code that are after the server call to wait.

What I've done:

async function func(){
   await google.script.run.withSuccessHandler(myFunc).serverFunc();
   console.log("done");
}
func();

How can the code wait to execute the console.log line until after the server side function resolves?

Alan Wells
  • 30,746
  • 15
  • 104
  • 152
  • does your `serverFunc()` return a promise? Your code should wait for it to resolve as is – Nick Parsons Oct 15 '19 at 04:41
  • You'll need to wrap your "procedure" (ie the code inside `serverFunc()` in a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), and `resolve()` when the "procedure" is complete. Make your `serverFunc` return this promise so that it can then be awaited. – Nick Parsons Oct 15 '19 at 05:35
  • @Nick Parsons At `google.script.run.withSuccessHandler(myFunc).serverFunc()`, at first, `serverFunc()` is run. This is Google Apps Script and this is run the server of Google side. Unfortunately, this doesn't return promise. As the next step, when no error occurs at `serverFunc()`, `withSuccessHandler` is run and the function of `myFunc` is run. [Ref](https://developers.google.com/apps-script/guides/html/reference/run) – Tanaike Oct 15 '19 at 06:54
  • 1
    Possible duplicate of [Is there a way to get the values out of a success handler without calling another function?](https://stackoverflow.com/questions/55247839/is-there-a-way-to-get-the-values-out-of-a-success-handler-without-calling-anothe) – tehhowch Oct 16 '19 at 23:42
  • https://ramblings.mcpher.com/gassnippets2/organizing-asynchronous-calls-to-google-script-run/#Organizing_with_promises – Bergi Dec 09 '20 at 14:52

3 Answers3

5

How about this answer? Please think of this as just one of several answers.

Pattern 1:

In this pattern, after serverFunc was run, myFunc is run. At that time, console.log("done") is run in myFunc.

function myFunc() {
   console.log("done");
}

function func(){
   google.script.run.withSuccessHandler(myFunc).serverFunc();
}

func();

Pattern 2:

In this pattern, Promise was used. When you run func(), you can see ok and done in order.

function myFunc() {
  return "ok";
}

async function func() {
  await new Promise(r => {
    google.script.run.withSuccessHandler(r).serverFunc();
  });
  const res = myFunc();
  console.log(res);
  console.log("done");
}

func();

Note:

  • If you test above samples, please set the function of serverFunc() at Google Apps Script side.
  • This is a simple sample script. So please modify this for your actual situation.

References:

If this was not the direction you want, I apologize.

Added:

If you want to use the values from serverFunc at myFunc, how about the following sample script?

Sample script:

function myFunc(nice) {
  doStuffs(nice);

  return "ok";
}

async function func() {
  const e = await new Promise(r => {
    google.script.run.withSuccessHandler(r).serverFunc();
  });
  const res = myFunc(e);
  console.log(res);
  console.log("done");
}

func();
  • In this script, the returned value from myFunc can be retrieved by res.
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • in my case, myFunc has a parameter which is the return value of serverFunc() like this: function serverFunc(){ doSomething(); return "nice"; } function myFunc(nice){ doStuffs(nice); } and i called it like this before: google.script.run.withSuccessHandler(myFunc).serverFunc(); in this case, myFunc is a callback function, and if i apply your solution, there's an error on it because ... () => r(myFunc())) .... (no parameter on it) thanks for the help :) – Makoto Daiwa Ambara Oct 16 '19 at 08:37
  • @Makoto Daiwa Ambara Thank you for replying. I apologize for the inconvenience. From your replying, I could know that you try to use the pattern 2. For this, I added one more sample script for your replying. Could you please confirm it? If that was not the result you want, I apologize. – Tanaike Oct 16 '19 at 22:32
  • it works with no error, but if i apply this: `console.log(res); otherFunc(); console.log(done);` the otherFunc() doesn't work with no error, except I use setTimeOut which is makes the whole promise setup being useless. this is the code inside otherFunc(), probably could extract some ideas why this happen. – Makoto Daiwa Ambara Oct 17 '19 at 01:05
  • `var btns = document.getElementsByTagName('button'); var mdls = document.getElementsByClassName("modal_detail"); var cds = document.getElementsByClassName("close_detail"); for(var i = 0;i – Makoto Daiwa Ambara Oct 17 '19 at 01:05
  • i found the problem, it doesn't work because btns, mdls, and cds are null because myFunc hasn't complete yet – Makoto Daiwa Ambara Oct 17 '19 at 01:25
  • 1
    please visit my [new issue](https://stackoverflow.com/questions/58423846/simple-promise-callback-google-script-function) – Makoto Daiwa Ambara Oct 17 '19 at 02:04
  • @Makoto Daiwa Ambara Thank you for your response. I would like to confirm it. – Tanaike Oct 17 '19 at 02:05
0

This code includes error handling and two Promise.then() methods. The example given below is the complete code for an Apps Script Web App, that I used for testing purposes to make sure the that code works. I needed a way to duplicate some client side code that uses fetch().then(), replacing the fetch(url) call to using Apps Script google.script.run.fncName().

H_Index

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <button onclick="some_Object_Name.innerNameOne()">Run</button>
    
    <div id="idRslt1">Result One</div>
    <div id="idRslt2">Result 2</div>
    <div id="idError">For error</div>
    
    <script>


function callServerAndGetRslt(po) {
  
  /*
    po.fncName - The name of the fnk to run
  
  */
  
  //console.log('po.fncName ' + po.fncName)
  
  return new Promise (function (resolve,reject) {
    google.script.run
      .withSuccessHandler (function (result) {
        console.log('result 24' + result)
        resolve (result);//What resolve does is return the result from the server back to the FIRST anonymous function in the "then" part
      })
      .withFailureHandler (function (error) {
        console.log('error: ' + error)
        reject (error);
      })[po.fncName](po);//There can NOT be anything inbetween the array and the ending parenthesis 
    })
}

function showError(err) {
  document.getElementById('idError').textContent = err;
  
}

function showResult(toShow) {
  document.getElementById('idRslt1').textContent = toShow;
  
}
function showResult2(toShow) {
  document.getElementById('idRslt2').textContent = toShow;
  
}

window.some_Object_Name = {

  innerNameOne : function() {
    return callServerAndGetRslt ({"fncName":'theFirstServerFncCall'})
      .then (function (result) {
        console.log('result: 45' + result)
        showResult (result);
      
        return callServerAndGetRslt ({"fncName":'serverFncCall2'});
      },//THERE MUST BE A COMMA HERE!!!!  This is a list of functions seperated by a comma
      function (error) {//Because this is the second function this is what gets called for an error
        showError(error);
        return "There was an error in call 1";
      }
      ).then (function (result) {
        showResult2("end result:" + result);
      });
  }
}

  </script>
  </body>
</html>

GS_Test

function theFirstServerFncCall(po) {
  Logger.log('po 1: ' + JSON.stringify(po))
  //throw new Error("err in first fnk");//This is for testing the 
  //error handling on the client side

  return ["name1","name2"];
}

function serverFncCall2(po) {
  Logger.log('po 2: ' + JSON.stringify(po))
  return [["one","two"]];
}

Code

function doGet() {
  return HtmlService.createHtmlOutputFromFile("H_Index");
}
Alan Wells
  • 30,746
  • 15
  • 104
  • 152
0

Thanks! This also solved my problem with lagged results from server for my dropdown values. Here is my code:

function createDropdowns() {
    
    loaddropdown("Dropdowns!A2:A","ctype");
    loaddropdown("Dropdowns!B2:B","state");
    loaddropdown("Dropdowns!C2:C","city");
    loaddropdown("Dropdowns!D2:D","remedies");
    loaddropdown("Dropdowns!E2:E","keywords");

    }
    async function loaddropdown(range,eid) {
    
    const e = await new Promise(r => {
    google.script.run.withSuccessHandler(r).getDropdownList(range);
    }); 
    filldropdown(e,eid);
}

//POPULATE HTML DROPDOWN

function filldropdown(values, elemid) { 
    
    var list = document.getElementById(elemid);   
    for (var i = 0; i < values.length; i++) {
    var option = document.createElement("option");
    option.value = values[i];
    option.text = values[i];
    list.appendChild(option);
    
    }
}
Martin Brisiak
  • 3,872
  • 12
  • 37
  • 51
Leo
  • 1