Skip to content

Instantly share code, notes, and snippets.

@schlos
Forked from peterherrmann/outerLoop.js
Created April 30, 2020 19:53
Show Gist options
  • Save schlos/3d0a9c489c3f233439d02f56f321a747 to your computer and use it in GitHub Desktop.
Save schlos/3d0a9c489c3f233439d02f56f321a747 to your computer and use it in GitHub Desktop.
outerLoop for Google Apps Script triggered functions is some boilerplate that encapsulates best practice ways to deal with running workloads that may take longer to process that the time available in an Apps Script triggered run.
//load configuration details and start logging - creates and sets up sheets the first time they are run
var CONFIG_SPREADSHEET_KEY = '<ssid_goes_here>';
var Config = SettingsManager.load(CONFIG_SPREADSHEET_KEY); //Add Mafviu9bMfg9xVu21LGfpWnHAGDwXQ1CH in Resources > Libraries
Logger = BetterLog.useSpreadsheet(Config['logSpreadsheetId'].value);//Add MYB7yzedMbnJaMKECt6Sm7FLDhaBgl_dE in Resources > Libraries
// trigger this function
function outerLoop() {
try {
// to calc elapsed time
var isOverMaxRuntime = false,
startTime = new Date();
// Deletes all occurrences of the Repeating trigger we don't end up with undeleted time based triggers all over the place
ScriptApp.getProjectTriggers().forEach(function(i) {
if (i.getHandlerFunction()==='outerLoopRepeating') {ScriptApp.deleteTrigger(i);}
});
//Logger.finer('Entering the "%s" function', arguments.callee.name);
// Handle max execution times in our outer loop
// Get start index if we hit max execution time last run
var start = parseInt(PropertiesService.getScriptProperties().getProperty(arguments.callee.name + "-start")) || 0;
var thingies = ['stuff to process', 'in an Array',,,,]; //
for (var i = start ; i < thingies.length; i++) {
if (Math.round((new Date() - startTime)/1000) > 300) { //360 seconds is Google Apps Script max run time
//We've hit max runtime.
isOverMaxRuntime = true;
break;
}
//do our work here
Logger.finest('Inside the for loop that does the xyz work. i is currently: %d', i);
var processingMessage = Utilities.formatString('%d of %d thingies: %s <%s>', i+1, thingies.length, thingyName, thingyId);
//do our work above here
}
if (isOverMaxRuntime) {
//save state in user/project prop if required
PropertiesService.getScriptProperties().setProperty(arguments.callee.name + '-start', i);
//create another trigger
ScriptApp.newTrigger('outerLoopRepeating').timeBased().everyMinutes(10).create();
Logger.info('Hit max run time - last iteration completed was i=%d', i-1);
} else {
Logger.fine('Done all the work and all iterations');
PropertiesService.getScriptProperties().deleteProperty(arguments.callee.name + '-start');
Logger.info('Completed processing all %d things with the "%s" function', thingies.length, arguments.callee.name);
}
} catch (e) {
Logger.severe('%s. While processing %s', JSON.stringify(e, null, 2), processingMessage);
throw e;
}
}
//automatically invoked from outerLoop()'s creation of a new trigger if required to get work done
function outerLoopRepeating() {
outerLoop();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment