In Use promise instead of callback for settimeout I showed how a more structured approach to scheduling events could be achieved through the use of promises rather than timeout callbacks.

In apps that use HtmlService, such as webapps or add-ons, there is no persistence between what’s happening on the Server and the Client so often the only answer is to use setTimeout to regularly poll the server and ask. So that means using google.script.run, which is also asynchronous. If you also have some activity which is asynchronous based on what the server is doing then that’s 3 or more asynchronous streams that need to be managed, leaving you in a callback hell. If you have multiple types of polling with different intervals, as for example I have in my Dicers, Sankey Snip, Chord Snip and Color Arranger add-ons, then it can start to become unmanageable.

Promises

I’ve covered introduction to Promises a number of times so I won’t repeat that here, but the problem we’re trying to solve can be expressed as

wait a bit, then poll and wait for the answer, then process the result, then repeat

This can be nicely expressed as a series of Promises like this

Waiting

In Use promise instead of callback for settimeout I showed how to convert timeout into a promise. Here’s the implementation of that from my Provoke namespace.

Now we can start coding our Client app, by waiting 5 seconds.

Polling the server

Now that we’ve waited a bit, we can poll the server. Instead of using google.script.run() here, which will generate a success and failure callback, I’d rather convert that into a Promise too. In my Provoke namespace, I have the .run method which wraps google.script.run in a Promise pattern like this.

It’s generalized so it needs to be able to run a given function as in google.script.run.yourFunction(args,…). Normally, google.script.run needs the function it wants to run to be in the globals space, but I usually avoid this as much as possible to avoid name clashes when reusing code. So to be able to cope with namespaces on the server and variable numbers of arguments, I have a single server side runner function in the global space called exposeRun. It looks like this (but see later in the post for a locked down version that uses whitelists)

Let’s say then that the function I want to call is in the Server namespace, and is called getData and returns all the data in the currently active sheet – it would look something like this

Now we can extend the client side code to this

Processing the result

I’ll leave out the definition of processing the data as it depends on your app, but it might be something like modifying it on the client, and writing it back to the Server, so we’ll need a setData function back in the server namespace to deal with that, which might look something like this (although you’d want some additional checks about valid data and that the sheet you are writing to is the one you expected to).

So now our client side App looks like this

Recursing

We can recurse the polling function to do it all over again

Catching Errors

Finally, we need to handle any errors. With a series of .then as we have, any errors occurring will cascade through to the end so we only really need one catch. Note that the syntax is actually .catch, but if you are using the Apps Script IDE to develop this in a .gs file, it doesn’t like .catch in that spot, so you can get round it by using [‘catch’] as in the example.

When you think about how this would look with a series of nested callbacks, I think you can appreciate how this approach simplifies the communication between server and client. Of course almost all the code and patterns are resusable – so your 2nd and subsequent add-ons are a breeze.

Some other tweaks

It’s better to only pass data over if its changed, so you can use a checksum as I describe in Watching for server side changes from the client html service, and that way it’s easy for the client to let the server know what it already knows about and avoid passing unchanged data back and forth. Switching off polling if the user is not active is another good tweak described in the same post.

Whitelisting

I use a version of exposeRun that has a whitelist of what’s allowed to be called from the client, partly to lock it down, but also to help with debugging and documentation. Here’s the version from my dicers add-on, which validates that the namespace/method combination being provoked from the client is one that is allowed to be called.

For more like this see Google Apps Scripts Snippets
Why not join our forum, follow the blog or follow me on Twitter to ensure you get updates when they are available.