However you can use Promises for client side code when using HtmlService – the IFrame mode allows it to run just fine. You just need to make sure you include a polyfill, in case it’s running on an old browser.
Using promises with google.script.run
If you use google.script.run, you’ll know this is a way to make calls to server side functions. What’s really good about this is that it’s asynch, so this means you can make multiple calls simultaneously – a great way to make your web apps more responsive, but it’s a bit messy since you have to have global functions to call, and you also have to deal with callbacks.
But you can use promises instead – here’s how.
An example – this runs Server.getData (100);
Provoke.run ( 'Server', 'getData' , 100) .then (function (result) { //do something with the result }, function (err) { //do something with the error });
What’s nice about this approach is that Provoke.run returns a promise, so you can use things like promise.all() to wait until a number of tasks have all completed – for example (a null namespace argument means its a global server side function)
Promise.all ( [ Provoke.run('Server','getData',100) , Provoke.run(null, 'findThis', 'abc',25), Provoke.run ('Logger','log','something')] ) .then (function(results) { // do something with all the results });
Later on I’ll show some example of some cool webapps using this approach.
The code
In your html file you’ll need the polyfill in case it’s running on old browsers
In your client side code you’ll need this provoke namespace
/** * @namespace Provoke * promise management for async calls */ var Provoke =(function (ns) { /** * run something asynchronously * @param {string} namespace the namespace (null for global) * @param {string} method the method or function to call * @param {...} the args * @return {Promise} a promise */ ns.run = function (namespace,method) { // the args to the server function var runArgs = Array.prototype.slice.call(arguments).slice(2); console.log(runArgs); if (arguments.length<2) { throw new Error ('need at least a namespace and method'); } // this will return a promise return new Promise(function ( resolve , reject ) { google.script.run .withFailureHandler (function(err) { reject (err); }) .withSuccessHandler (function(result) { resolve (result); }) .exposeRun (namespace,method,runArgs); }); }; return ns; })(Provoke || {});
and you’ll also need this server side global function.
/** * used to expose memebers of a namespace * @param {string} namespace name * @param {method} method name */ function exposeRun (namespace, method , argArray ) { var func = (namespace ? this[namespace][method] : this[method]) if (argArray && argArray.length) { return func.apply(this,argArray); } else { return func(); } }
Whitelisting/blacklisting
If you want to make sure that only certain methods or namespaces are ever invoked from the client side with exposeRun, then you can do that like this example. For example my Apps only ever allow functions in the Server namespace to be provoked, so I have a slightly modified version of exposeRun
/** * used to expose memebers of a namespace * @param {string} namespace name * @param {method} method name */ function exposeRun (namespace, method , argArray ) { if (namespace !== "Server") { throw new Error ('Tried to execute method ' + method + ' from prohibited namespace' + namespace) } var func = (namespace ? this[namespace][method] : this[method]) if (argArray && argArray.length) { return func.apply(this,argArray); } else { return func(); } }
And that’s it, you’re using Promises to call Apps Script Server side functions!