-
-
Save sdesalas/2972f8647897d5481fd8e01f03122805 to your computer and use it in GitHub Desktop.
/* | |
* 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); | |
} | |
}); | |
}; |
Hi @Tamarindo94
Only primitives will work as callback arguments, however its relatively straightforward to pass the ID and then recreate the Spreadsheet object inside the handler method.
Exception: This script has too many triggers. Triggers must be deleted from the script before more can be added.
I just copied your code
Did the error message appear because the google script was updated? Or am I doing something wrong?
Thanks
The most likely explanation for your script having too many triggers is because you are calling the Async function inside a loop.
See comment above
Triggers are expensive things for GAS to keep track off so I would avoid putting Async.call(blah) inside a loop.
A better way to acomplish this would be to put the for..loop inside the asynchronus function you are calling from within your createBatchScript function, so you dont create a zillion triggers inside a loop but just a single one.
You should be able to go into the list of Triggers
for your script (side menu) and delete them one by one.
u should be able to go into the list of
Triggers
for your script (side menu) and del
Thank you so much for your reply!
I was looking for a solution to speed up data processing in my table by running several algorithms at once.
The solution was 2 things:
- Run Google apps script in 2 browser tabs and run code in them from one file (it works even with 3 or more browser tabs) Such multi-threading =)
- I switched to python, though. Everything is much faster there, and there is no limit on the algorithm in 6 minutes.
I tried implementing this code. If I call Async.apply directly while testing, it works fine. If placed then call into MyOnOpen.gs which is itself invoked by a trigger. This does not seem to work and I get the error message: 'Script function not found: Async_handler'
Can anybody provide me direction on how to fix this?
@sdesalas Hi! I tried to apply your async solution, but on first iteration an error pops up when calling the object's method. What do you think is the problem?
Error: [TypeError: folderSecond.createFile is not a function]
Code:
function uploadFilesFromDrive(url,filename,folderSecond){
const fileID = getIdFrom(url);
const getFile = DriveApp.getFileById(fileID);
const blob = getFile.getBlob().setName(filename).getAs('application/pdf');
const file = folderSecond.createFile(blob);
};
@ShadrinSpock see comment above
Only primitives will work as callback arguments, however its relatively straightforward to pass the ID and then recreate the Spreadsheet object inside the handler method.
In your case you probably want the ID of the folder. Instead of the folder object.
@sdesalas Thank you so much for writing this! I've only just begun my journey as a developer and learned how to write asynchronous functions in Javascript today, only to find that it doesn't work in Google Apps Script. Your library worked like a charm for my purposes though. I managed to get the expected result on the first try, which is honestly incredibly surprising knowing me.
It seems so weird that you can write async function blah(), or return promises and write .then(), but Apps Script still runs it synchronously. Do you know why that is? What is the point of having the syntax if it doesn't work the way it was intended?
Is there a way to pass complex objects as arguments to Async.call ? My script works fine as long as I pass strings as parameters, but if I pass an object of type Spreadsheet, for example, the handler function sees an empty object instead of the Spreadsheet...