I have a Google App Script function in a Google Sheets spreadsheet I need to call once a day using a time-driven trigger.
This function often takes longer to run than the maximum time allowed for scripts, currently 6 minutes, so I've written it to do its work over several calls. If the function hasn't finished I would like to create a temporary time-driven trigger to run the function again in one minute and delete the temporary trigger when the function is called, but leave the daily trigger active. Pseudocode probably explains it better...
function run_job_via_trigger(trigger) {
if(trigger === temporary trigger) {
// If this is a 'temporary' trigger that was created to
// run the job after the first call then delete it.
// This must not delete the daily trigger that makes the
// first call to the function.
// If I check the UID of the trigger here I still
// would need to know which trigger is the daily trigger
// and which is a temporary trigger!
ScriptApp.deleteTrigger(trigger)
}
const job_finished = job_that_takes_several_calls_to_complete();
if(job_finished === false) {
// Create a temporary time-driven trigger to call this
// function again in 1 minute.
ScriptApp.newTrigger('run_job_via_trigger').timeBased().everyMinutes(1).create();
}
}
function job_that_takes_several_calls_to_complete() {
// This function often takes more time to complete than
// the maximum time allowed for scripts to run. It keeps
// track of its execution time and returns true if it has
// finished doing what it needs to do or false if it
// needs more time and should be called again.
return finished ? true : false;
}
How can I detect which time-driven trigger has called the run_job_via_trigger function so I can delete the temporary trigger(s) but not the daily trigger?
The spreadsheet has several other time-driven triggers so simply deleting all triggers at the end and creating a new daily trigger is not, as far as I can tell, an acceptable solution.
When your function is called with a trigger, it receives a trigger event as its parameter. You can check the trigger UID for example, like so:
function doWhatever(e) {
if(e.triggerUid === 'trigger_id') {
// do something
} else {
// do something else
}
}
UPDATE
There are a couple of ways to know which triggers are running.
The best-case scenario, when you created a trigger, you stored its ID somewhere, like user properties and then you always know when it's running. But I guess you haven't done that.
In your case you might want to do some manual work. Go to the triggers page, find your recurring trigger, click on the three dots on the right and select "Executions". You will then see the trigger ID in the filter:
Now you can use that in your code to check whether it's your recurring trigger or your temporary trigger.
Related
I'm looking to allow a google docs user to turn the background of a given text range yellow by typing an exclamation point. This sounds like onEdit, which I know exists in sheets but not in docs. I saw this workaround on GitHub, but it requires adding a sidebar and inserting HTML, which I'd rather not do.
This answer discusses an onEdit workaround, but it still has the trigger fire every 60 seconds, rather than, say, every second.
This answer lays out how to call a function every second, and I'm trying to get it to work, but I can't figure it out. Here's what I have:
function myFunction() {
for (var i = 0; i < 10; i++) {
var doc = DocumentApp.openByUrl('Doc URL');
var body = doc.getBody();
var text = body.editAsText();
Logger.log(body.findText('!'))
if (body.findText('!') != null) {
text.setBackgroundColor(13, 50, '#FFFF00');
Utilities.sleep(1000);
}
ScriptApp.newTrigger("myFunction")
.timeBased()
.after(1000)
.create();
}
}
The function runs once, then sends me the error message, "Exception: This script has too many triggers. Triggers must be deleted from the script before more can be added." I don't have any triggers added other than what's in this function. I suspect the answer might be a while loop instead, but I'm a beginner and I'm not sure. What should I do?
The function you show will create ten triggers that run that very function after a second. When those ten copies run, they each create another ten triggers. So after one second you would have 10 triggers, after two seconds you would have 100 triggers, after three seconds you would have 1000 triggers, and so on. In practice, you get an error like the one you mention almost right away.
What you need is exactly one trigger. Use getProjectTriggers() to find whether a trigger already exists, and only create a trigger if there are none. You should also move the trigger creation code outside the loop.
I have an API function to extract data from Amazon, I need to have 'fetch' & 'get' report function separately to achieve action for
request fetching report from Amazon, and
get report from Amazon, and the request action take about 2 mins for Amazon to finish the fetching request and allow for getting the report, so in the script I have set script.newtrigger after 2 minutes.
function fetchReportMarketListTrig() {
fetchReportMarketList(true);
ScriptApp.newTrigger("getReportsForAccountMarketListTrig")
.timeBased()
.after(2* 60 * 1000)
.create();
Logger.log("Report Request Created");
}
The function worked perfectly when I manual trigger it from menuitems, getReportsForAccountMarketListTrig will be called after 2 minutes. However, if I set auto trigger, fetchReportMarketListTrig cannot call getReportsForAccountMarketListTrig. we need to seek the answer of why the sheet is acting different from manual trigger and auto trigger. Thanks a lot!
Manual trigger from menuitems
Auto trigger set in google sheet time-driven trigger
Auto Trigger and Manual trigger execution result
Answer:
You have two functions with the same name! This produced undefined behaviour when you create a trigger for this function.
More Information:
In the screenshot you provided for your trigger, you are displayed the following warning:
Functions within the same project must always have a unique name!
Imagine the following code:
function doSomeMath() {
var x = 1 + 2;
console.log(x);
}
function doSomeMath() {
var x = 100 + 200;
console.log(x);
}
And then you ask your trigger to run function doSomeMath() - how does it know which one you mean?
Fix:
The likelyhood here is that the other function in your code canned fetchReportMarketListTrig is what is running on the time-based trigger, and not the one you intend to have run.
To fix this, search your script file for the functions named fetchReportMarketListTrig, and rename them so that they are unique. You can then set up your trigger on the function you wish as normal.
I would like to edit the timing of a trigger based on a value in a cell of a spreadsheet. The only way I can think of is to set a trigger for a function that is deleting all triggers and making new triggers based on the value of a cell. Is there a way to edit the existing timing of existing triggers?
You can delete just one.
function deleteTrigger(funcName)
{
var triggers=ScriptApp.getProjectTriggers();
for(var i=0;i<triggers.length;i++)
{
if(funcName==triggers[i].getHandlerFunction())
{
ScriptApp.deleteTrigger(triggers[i]);
break;
}
}
}
I run things at variable time intervals by setting my trigger to the minimum interval I need, tying the trigger to a function that tests against criteria. If criteria is met, I execute the desired function. You could implement this principle where the criteria for whether to execute the main function is dependent on the cell contents (or direct it to different functions depending on the contents of the cell).
I would like to get a trigger to run a specified amount of time after an edit occurs.
I'm thinking that I could someone combine an edit trigger and a time based trigger for this functionality, but am not clear how.
To provide some additional detail.
I have a Google Sheet where I track my insulin usage. I am supposed to check my blood sugar 2 hours after I administer my insulin.
When I administer the insulin I make an entry into Google Sheets, time/amount/etc... When I make this entry I would like to create a trigger that will run in 2 hours to send me an email notification as a reminder to check my blood sugar again and make a new entry.
This is how I use time triggers in Google App Script.
Function to create the time trigger.
function createTriggger(name,action,time){
// Time is in minutes
var trigger = ScriptApp.newTrigger(action)
.timeBased()
.after(time*60*1000)
.create();
var triggerID = trigger.getUniqueId();
// Store trigger id to delete the trigger once it has executed
PropertiesService.getScriptProperties().setProperty(name, triggerID);
};
This name is the name of the property to store the trigger id, so you can delete the trigger after it executes. action is the name of the function you want the trigger to execute, and time is in minutes.
On the last line of your function that you want the trigger to execute, use this line to grab the trigger id and delete it from your trigger list. Put the name of your trigger id property in name.
var triggerID = PropertiesService.getScriptProperties().getProperty(name)
deleteThisOne(triggerID)
Here is the delete funciton:
function deleteThisOne(id){
var triggers = ScriptApp.getProjectTriggers();
for(var i=0;i<triggers.length;i++){
if(triggers[i].getUniqueId() == id){
ScriptApp.deleteTrigger(triggers[i]);
break;
};
};
};
Look at google scripts Installable Triggers for Limitations
When your edit trigger fires, you could use a ClockTrigger - see the ClockTriggerBuilder documentation and example, which allows you to either set a time relative to the current execution time, or at an absolute time.
You can use a work around like this:
Set '0' value in some corner cell in spreadsheet which you won't modify ever. Hide that column.
Set onEdit trigger on spreadsheet and write its function, which sets the value '0'.
Set another trigger which is triggered every 10 minutes to increment that cell value by 10 every time. Also add this to that function:
if(cellValue%120 == 0)
{
setReminder(); //function to send you reminder
cellValue = 0;
}
I have a function that updates a spreadsheet. It runs every morning and, every time it does, it creates 4 triggers.
I also have time-driven triggers, which I want to keep there.
Since there is a limit on the triggers, I need to remove the triggers created by the function (while maintaining the other ones). However, when I run this:
function Triggers () {
Logger.log(ScriptApp.getProjectTriggers())
}
I get this as a response:
[Trigger, Trigger, Trigger, Trigger, Trigger, Trigger, Trigger, Trigger]
How can I identify, of these, the ones created by that function so I can eliminate those only?
I used this code:
function Triggers () {
var triggers = ScriptApp.getProjectTriggers()
for (i in triggers)
if ((triggers[i].getHandlerFunction()) == "createStats") {
ScriptApp.deleteTrigger(triggers[i])
}
}