1

In Google apps script when using a client sided .HTML file you can call a server sided script using google.script.run.(Function name).

You can see the related documentation here: https://developers.google.com/apps-script/guides/html/reference/run

Now this script has been working with no problems over the first 6 months of its lifetime or so. I have not touched the program and I have not been notified or have located any newly deprecated code.

Over the course of the last couple months however, my users have been reporting that when they finish interacting with the HTML document, nothing happens when they close it and they have to repeat the entire process 3 or sometimes even 4 times before they will get it to go through., This means that when the user closes the client sided HTML window, the server sided function should be called to handle the remaining tasks but in some cases is not. This issue is completely random, and does not seem to be caused by anything specific.

I have taken some steps myself to attempt to solve the issue. I have wrapped the entirety of the code in try catch blocks, including the .HTML and .GS files. This means that if literally ANYTHING goes wrong in ANY script, I will be notified of it immediately. However, despite this being the case I am yet to receive any emails of it failing even though I watch it fail with my own eyes. I have added log commands before and after this function to see if it stops working all together or continues. In every case regardless of whether the function call is successful or not the log commands go through.

To me this can only mean that for some reason the function google.script.run is not working properly, and is failing to run the associated function, but is not returning an error message or stopping the script.

I am at an absolute loss since I have no error message, no reproducible steps, and no history of this being a problem before while suddenly starting to get worse and worse over time. I have checked Google's issue tracker to no results. If anyone else is using this function and is having problems I would love you to share your experiences here. If you have a solution please let me know as soon as possible. If I can't fix this issue I am going to have to use a new platform entirely.

Edit 10/2: After looking further into this issue I have discovered a list of all executions on this project. I can see what functions were executed, when, and how long they took to execute. I can see that when the function that opens the HTML service is ran, the next function that should run does not always appear in the list. And when it doesn't, I can see that the user repeated their steps until it did run. This supports my theory that the function just isn't running when it should be after being called my script.run

TheMaster
  • 45,448
  • 6
  • 62
  • 85
Swordstoo
  • 855
  • 1
  • 8
  • 10
  • 2
    How many concurrent users? At most 30 simultaneous executions are allowed. – tehhowch Oct 02 '18 at 11:17
  • 3
    Without seeing your code it is difficult to determine the problem. But it is highly unlikely google.script.run "is not working properly", since it is used extensively by many users. And my experience is try catch in the client side javascript may not always trap the error. Another thing is how the asynch client and server functions are run. You are using withSuccessHandler and withFailureHandler aren't you? – TheWizEd Oct 02 '18 at 16:19
  • @Tehhowch the number of concurrent users never exceeds 2 or 3. Is this limit account wide accross all scripts or just on each function? – Swordstoo Oct 02 '18 at 18:22
  • @theWizEd yes, I am using both handlers to determine the cause. When Google.script.run is not run, the failure handler does NOT kick in. The success handler runs each time regardless of the outcome. – Swordstoo Oct 02 '18 at 18:23
  • @TheWizEd Here is a basic copy of the script that utilizes google.script.run: function onFailure(error) { MailApp.sendEmail("sparkycbass@gmail.com", "Order book eror", "ERROR: " + error.message); google.script.host.close(); } function handleFormSubmit(formObject) { google.script.run.withFailureHandler(onFailure).processForm(formObject) google.script.host.close(); } – Swordstoo Oct 02 '18 at 18:38
  • @TheWizEd I can't get the markdown text to behave so here is a pastebin link: https://pastebin.com/jvmGZv2X. For more context, google.script.host.close(); runs without failure each time, meaning that google.script.run functions without issue, and I have never had the onFailure(error) kick in as I have never received an email. – Swordstoo Oct 02 '18 at 18:44
  • 1
    `MailApp.sendEmail()` won't work in client side code. Your `onFailure` function has `MailApp.sendEmail()` in it. You need to make a `google.script.run.functionName()` call to the server from `onFailure` and then send the email from the server. – Alan Wells Oct 02 '18 at 22:16
  • Ok, I will do this to try and see if I can get an email from future errors. Thanks @SandyGood – Swordstoo Oct 02 '18 at 22:18
  • So it looks like that even though I have intentionally caused the function handleFormSubmit to fail, .withFailureHandler(onFailure) never runs regardless. The function never even runs according to execution transcripts and execution history.. – Swordstoo Oct 02 '18 at 22:32
  • More information that might be handy: I have attempted to run google.script.run.withSuccessHandler() and .withFailureHandler() and neither of them do anything. I even copy and pasted the documentation example into a controlled spreadsheet and I do not get any results out of any test no matter what I change the code to. I truly think something is wrong with google.script.host. Even If I intentionally error the code out (and can see it failed in the execution transcript) or if I do nothing and let it end successfully it is still not doing anything whatsoever. – Swordstoo Oct 02 '18 at 23:50
  • Here is a spreadsheet with my testing area that has no results whatsoever https://docs.google.com/spreadsheets/d/1ouzTEnsijdSb1mI5-8FfX6kk0kycLuOAfvfxIu2P1Wc/edit#gid=0 – Swordstoo Oct 02 '18 at 23:53
  • [Edit] your question to include the code and the comments – TheMaster Oct 03 '18 at 01:07
  • I included it in a public spreadsheet and a pastebin document – Swordstoo Oct 03 '18 at 04:03
  • @Swordstoo, why are you using doGet, it looks like a bound script to spreadsheet. It should be onOpen. There is no openDialog function. Move your HTMLService code to the openDialog function. And onFailureT should be in client script not Code.gs. Its the script that is run when processForm succeds and returns. – TheWizEd Oct 03 '18 at 13:59
  • 1
    @Swordstoo, another thing I noticed about your client script is it calls google.script.host.close right after processForm. Since client server scripts are run asynchronously, your form will close before processForm returns. So your sucess handler affectively does nothing. – TheWizEd Oct 03 '18 at 14:12
  • @Swordstoo, I've edited the script files of your spreadsheet to make it work. – TheWizEd Oct 03 '18 at 16:30
  • @TheWizEd I was using doGet because that was the documentations function name. In the script you edited, your modifications do work in the specific situation. However, if I uncomment the line that causes a runtime error the success handler still goes through. Shouldn't the successhandler not trigger if onFailureT has a runtime error? – Swordstoo Oct 03 '18 at 17:56
  • @Swordstoo, doGet is a required function for a web app (unbound script). For a script bound to a spreadsheet onOpen is a simple trigger that runs when the file opens. I found that you had a function linked to a button. You could link the openDialog function directly to the button. I'm unclear as to what you mean by runtime error and success handler. If the server script runs successfully and returns it runs the function designated as the success handler, if it fails it would run the failure handler. onFailureT is a success handler, not a failure handler. – TheWizEd Oct 03 '18 at 21:37
  • @Swordstoo, you should look at 2 sites https://developers.google.com/apps-script/guides/triggers/ and https://developers.google.com/apps-script/guides/html/reference/run – TheWizEd Oct 03 '18 at 21:37
  • Related Q's: Related Questions: https://stackoverflow.com/questions/37783193/html-service-submit-form-not-calling-google-script-run-function https://stackoverflow.com/questions/27001676/apps-script-cant-use-google-script-run-from-within-a-js-function-in-an-html-t – tehhowch Oct 05 '18 at 21:13

2 Answers2

2

In the comments you posted this function snippet:

Here is a basic copy of the script that utilizes google.script.run:

function onFailure(error) {
  MailApp.sendEmail("sparkycbass@gmail.com", "Order book eror", "ERROR: " + error.message);
  google.script.host.close();
}
function handleFormSubmit(formObject) { 
  google.script.run.withFailureHandler(onFailure).processForm(formObject)
  google.script.host.close();
}

The problem here is that google.script.run is asynchronous - the call to your server-side function processForm is not guaranteed to be even initiated before the call to google.script.host.close() is made:

Client-side calls to server-side functions are asynchronous: after the browser requests that the server run the function doSomething(), the browser continues immediately to the next line of code without waiting for a response. This means that server function calls may not execute in the order you expect. If you make two function calls at the same time, there is no way to know which function will run first; the result may differ each time you load the page. In this situation, success handlers and failure handlers help control the flow of your code.

A proper pattern is to only call "destructive" commands - such as closing the host and therefore unloading all the relevant Apps Script instances - after the server has indicated the async operation completed. This is within the success handler of the google.script.run call:

.html

function onFailure(error) { // server function threw an unhandled exception
  google.script.run.sendMeAnEmail("Order book error", "ERROR: " + error.message);
  console.log(error);
  document.getElementById("some element id").textContent = "There was an error processing that form. Perhaps try again?"
}
function onSuccess(serverFunctionOutput, userObj) {
  // do stuff with `serverFunctionOutput` and `userObj`
  // ...
  google.script.host.close();
}
function handleFormSubmit(formObject) {
  google.script.run
    .withFailureHandler(onFailure)
    .withSuccessHandler(onSuccess)
    .processForm(formObject);
}

.gs

function processForm(formData) {
  console.log({message: "Processing form data", input: formData});
  // ...
}
function sendMeAnEmail(subject, message) {
  console.log({message: "There was a boo-boo", email: {message: message, subject: subject}});
  MailApp.sendEmail("some email", subject, message);
}
tehhowch
  • 9,645
  • 4
  • 24
  • 42
  • Hey @tehhowch I tried both variations of handlers but they didn't work consistently in my code, I'll have to do more research into the handlers before I can implement it – Swordstoo Oct 06 '18 at 18:01
1

Tl;dr: The affected computers are running so slowly that google.script.host.close would run before google.script.run.functionName() is able to be called and the information passed from the client to server, causing the function to never run but also not return an error. Adding Utilities.sleep(1000) fixes the issue.

I'm answering here in the situation that someone stumbles upon this thread in the future because they're having similar problems. I was able to fix the issue by adding two lines of code between google.script.run and google.script.host.close. I added Google's Utilities.sleep(1000) to force the computer to wait one second between executing the function and closing the HTML window. I also added an HTML alert that shows that the function was called and didn't suffer from a runtime error.

I don't know exactly why this seems to have fixed the issue but I have a theory. I have about 20 computers this spreadsheet runs on. Only about 6 of them were having the issue, and this wasn't brought to my attention until recently. As it turns out the 6 computers that were having the issue were the slowest computers of the bunch. My theory is that the computers were so slow, and the internet bandwidth was fluctuating so much that the computer simply didn't have time to call google.script.run and pass off the information from the client sided HTML window that it simply got closed and cut off when google.script.host.close was run. This means that the function will not exist in the execution transcripts or history, nor will there be any sort of runtime error. All of those things were true in my situation. This also explains why I never had the issue on any of my own equipment in a testing environment since it didn't suffer from any slowdowns the other computers were having.

By adding both Utilities.sleep(1000) and the UI alert this forces the javascript to not continue to google.script.host.close until the user interacts with the UI alert (Which is just a confirmation window with an OK button) and afterwards waits a full second. This sacrifices a tiny bit of user friendly-ness for a more functional script. Since I have implemented this "fix" none of my users are reporting any issues and all of my execution history looks just fine.

Hopefully this helps any future passerbys.

Swordstoo
  • 855
  • 1
  • 8
  • 10