Last active
April 29, 2025 01:24
-
-
Save sdesalas/2972f8647897d5481fd8e01f03122805 to your computer and use it in GitHub Desktop.
Asynchronous execution for Google App Scripts (gas)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Async.gs | |
* | |
* Manages asyncronous execution via time-based triggers. | |
* | |
* Note that execution normally takes 30-60s due to scheduling of the trigger. | |
* | |
* @see https://developers.google.com/apps-script/reference/script/clock-trigger-builder.html | |
*/ | |
var Async = Async || {}; | |
var GLOBAL = this; | |
// Triggers asynchronous execution of a function (with arguments as extra parameters) | |
Async.call = function(handlerName) { | |
return Async.apply(handlerName, Array.prototype.slice.call(arguments, 1)); | |
}; | |
// Triggers asynchronous execution of a function (with arguments as an array) | |
Async.apply = function(handlerName, args) { | |
var trigger = ScriptApp | |
.newTrigger('Async_handler') | |
.timeBased() | |
.after(1) | |
.create(); | |
CacheService.getScriptCache().put(String(trigger.getUniqueId()), JSON.stringify({ handlerName: handlerName, args: args })); | |
return { | |
triggerUid: trigger.getUniqueId(), | |
source: String(trigger.getTriggerSource()), | |
eventType: String(trigger.getEventType()), | |
handlerName: handlerName, | |
args: args | |
}; | |
}; | |
// GENERIC HANDLING BELOW | |
// | |
function Async_handler(e) { | |
var triggerUid = e && e.triggerUid; | |
var cache = CacheService.getScriptCache().get(triggerUid); | |
if (cache) { | |
try { | |
var event = JSON.parse(cache); | |
var handlerName = event && event.handlerName; | |
var args = event && event.args; | |
if (handlerName) { | |
var context, fn = handlerName.split('.').reduce(function(parent, prop) { | |
context = parent; | |
return parent && parent[prop]; | |
}, GLOBAL); | |
if (!fn || !fn.apply) throw "Handler `" + handlerName + "` does not exist! Exiting.."; | |
// Execute with arguments | |
fn.apply(context, args || []); | |
} | |
} catch (e) { | |
console.error(e); | |
} | |
} | |
// Delete the trigger, it only needs to be executed once | |
ScriptApp.getProjectTriggers().forEach(function(t) { | |
if (t.getUniqueId() === triggerUid) { | |
ScriptApp.deleteTrigger(t); | |
} | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @stallioninet,
I have not tested your approach, but I believe you don't need this library to achieve your desired result.
Google Apps Script has its own built-in API for making requests to external API's, and it will not execute code past the request if you are using their tools. With GAS, you don't need the traditional javascript async syntax to achieve some async results within standard functions. For example, every call you make to SpreadsheetApp or something similar is asynchronous, but the syntax doesn't require it. That is all handled under the hood.
Async.gs saves the function and the parameters you pass in at the time you call it and programmatically creates a time-based trigger that executes it as soon as possible. This is useful in many cases, such as when you need to execute things out of sequence, but you should not need it here. You would likely be better served by paginating through the data served by the API you are trying to contact and either writing it to the sheet as you receive it or storing it all in a 2-D array that is written after the pagination is complete.
Ensure that you use the built-in methods for interacting with external api's, run your code sequentially, and all should be well.