I’ve published a few examples of this in other projects (for example Implementing a client side progress bar reporting on server side work for htmlService) but I got a request from someone on LinkedIn to help explain how in principle to do this via a very simple example – this article will walk throughhow to do this in detail
HtmlService runs client side in the browser, and initiates server side functions via the google.script.run function. An important concept to grasp is that each server side script invocation is a completely new instantiation and doesn’t inherit anything from previous invocations. That means that there are no persistent global variables or any other technique that can help, so we need a different approach.
Here’s a server side simple script that simply sleeps for various times. What we want to do here is have a client side script continually reporting on the progress of the server side script. Later on I’ll make this a more interesting script.
There’s no way to update the client side directly from the server side, however we can control the server side from the client side. We can use the CacheService to periodically update progress on server side, and we can interrogate cache from the client side by invoking a different script, to see how far the target script has come along.
We’ll also need a unique id shared between client and server to use to reference the same cache entry as each other. I’m also going to use the user cache rather than the script cache. This will ensure disambiguation between different instances of the same script.
Running simultaneous server scripts (even the same script multiple times) is possible because each clent side call to google.script.run provokes a completely seperate instance of the script, and it’s this simultaneity that enables a script to query the status of another via the CacheService
Let’s enhance the server side script to write its progress to cache.
A minimal html file with just an element to receive messages from the server side
Client side script
I’m going to modernize google.script.run a little so we can use promises. This is a simplified version of the technique discussed in Apps Script V8: Provoke server side code from add-ons and htmlservice
We’ll be polling the server side occassionally, and could use setInterval. However, this is quite a blunt instrument as it will continue to poll server side even when there’s slowness in the previous response. I prefer to poll using an interval since the last response. That way we won’t overwhelm the server side and can react better to errors.
First of all we’ll need a promisified version of setTimeout
We’ll also need a function to know when its finished server side. We could do this by detecting when the main script has completed, but the progress reporting might be just a subset of that main script, so instead we’ll send a definitive signal from server side.
To make the polling more responsive, we’ll issue a repoll some time after the previous poll successfully finished.
Kicking off the main script and initiating polling
All that’s left is to kick off the main server side script, and start polling from the client side.
Here’s the entire index.html, now with the script constructed.
More useful version
In real life you’ll want to decorate the rendering of progress perhaps with a progress bar or something, and of course the server side script will be doing something useful. Let’s generalize the server side a little more.
Cache status payload
We’ll probably want to pass more than just a string – so we’ll update the get and set progress functions to handle objects if it detected them.
Again we’re just simulating tasks, but this time we’ll make each take a random amount of time, and write additional information about progress to cache. Using this will allow the client side to provide more info about progress.
The only real difference is that now we’ll be receiving an object with progress status rather than a string as before, so we need to do some formatting.
You’ll want to decorate the progress report, perhaps using some of the other examples on this site. In any case, there should be some useful examples of modernizing polling and google.script.run here to use in other projects.