Why is a second taking longer than a second? - google-apps-script

I'm trying to create a timer in Google Apps Script, such that when there is a number displayed on a cell, and I start the script, the script will automatically decrement that number once every second, until the value reaches zero.
So I found this answer on a related question, and managed to implement code that does the same thing just fine. My one problem is that the code seems to run slowly - so that a second doesn't actually take a second. At a guess, it's between 1.5 to 2 seconds. This obviously isn't hugely ideal for a timer, and I'm wondering if it's a fixable problem. Is there a code optimization that might make my timer run on time, or something I've not factored in?
Here's a link to a test spreadsheet, with the code in the Script Editor (it's View Only for safety, but the spreadsheet can be copied.) A UI control should initiate the "Start Timer" function if you want to test it.
Here's the full code:
var ss = SpreadsheetApp.getActiveSpreadsheet();
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu("Timer controls")
.addItem("Start Timer", "startTimer")
.addToUi();
}
function startTimer() {
var timer = ss.getRangeByName("Timer");
var timeval = timer.getValue();
while (timeval > 0) {
timeval--;
timer.setValue(timeval);
SpreadsheetApp.flush();
Utilities.sleep(1000);
}
}

Timer
function undertest() {
var start=new Date().getTime();//milliseconds
//all your other code here
Logger.log(Number((new Date().getTime()-start)/1000).toFixed(2));//seconds
}

Tl;Dr There is no fix for your code.
You are missing that there are communications processes between your web browser and Google servers...
and that Google Apps Script methods are slow,
and that editing a cell value triggers the recalculation of the whole spreadsheet.
You should start over and instead of using sever side code and Google Sheets to display a timer, follow the recommendation of Cooper on his answer to the same question of the answer that in linked in the question: Use client-side code.

Related

Google Sheet Error: Service Spreadsheets timed out while accessing document with id

I get this error "Service Spreadsheets timed out while accessing document with id ..." every time I run a very simple code, in which I am basically copying data from one google sheet to another using getValues() and setValues().
I don't think it is because of 5M cells limit, because the same exact function is working perfectly fine in another Google Sheet with even bigger size. So I really don't understand where the problem is.
I have tried to create an empty GS and run the function, so I am only pulling data without any other calculation, but still, it gives me the same error.
Any idea what the reason could be?
Here the code as reference:
function MyFunction(){
var pm_ss_0 = SpreadsheetApp.openById('...');
var pm_tab_0 = pm_ss_0.getSheetByName('...');
var pm_data_0 = pm_tab_0.getDataRange().getValues();
var target_ss_0 = SpreadsheetApp.getActiveSpreadsheet();
var target_tab_0 = target_ss_0.getSheetByName('...');
target_tab_0.clearContents();
var target_data_0 = target_tab_0.getRange(1, 1, pm_data_0.length,
pm_data_0[0].length).setValues(pm_data_0);
}
I solved the issue inserting a flush before and after the line where the error appeared.
SpreadsheetApp.flush();
ss.insertSheet("Report "+fogl.getName(), ss.getNumSheets()); //line with the error in my code
SpreadsheetApp.flush();
This issue has also been reported on Google's Issue tracker
Go there and star the issue so you get the updates on it.
This problem is more random than 95% of the commentary on the Web about it attests to. I just had this happen to me for the first time, and it even affected a Macro that did absolutely nothing but hide the Active Tab. I couldn't do anything with Script Editor.
I tried simply duplicating the document. BION, that was the end of the problem for me. Or at least, so far.

Looking for an onEdit script that can be used to change the color of a google sheets cell when it is edited?

I am using google sheets to monitor a project that includes multiple users. I would like to use an onEdit command to change a cell's color to yellow when the content is edited. Below is the code that I am working with. However, I am unable to get it to function. Coding is still new to me, so any advice would be greatly appreciated.
function onEdit1(e)
{
var range=e.range;
var column=range.getColumn();
if(column>3 && column<27)
{
range.setBackground('#ffff00')'
}
}
function onEdit(e){
if(e.range.getSheet().getName()!="Sheet1")return;
if(e.range.columnStart>3 && e.range.columnStart<27) {
e.range.setBackground('#ffff00');
}
}
I also added a line to limit it to only one sheet. You may wish to change the name of that sheet or even remove it entirely if you want it to run on your entire spreadsheet.
A lot of new programmers try to run these onEdit(e) functions from the script editor. Unfortunately, that doesn't work because the e parameter is expecting to be populated by the event trigger. Without the event object you'll normally get an error like Cannot read property range from undefined because e has not been populated by the event trigger.
I test them by making sure I'm editing the correct sheet and correct range and I use the e.source.toast() function to provide me with feed back sort of like the console.log() does.
If you want to learn more about the event object then try adding a Logger.log(JSON.stringify(e)); to the first line after the function declaration. And then get it to run by editing the appropriate sheet in the appropriate way and go to view log to see the results.

Try Catch for if script is already running in sheets, prevent a 2nd instance at same time?

I have a script that does some basic copy and pasting of data, creates a sheet as a pdf and attaches to an email.I have a button to start it for my less-tech-experienced colleagues embedded in a sheet:
We share the spreadsheet doc and I'd like to keep it that way for version control. What's the best way to set the script up so that if an instance of the script is already running in the spreadsheet doc it rejects a 2nd attempt until the first one is completed? Some sort of try catch?
I don't think the code is as relevant for this question, here's the beginning of the function in question:
function failedSettlementsEmailz() {
var contacts;
var toastMembers="";
....more code...
It calls two other functions in my script document that is bound to the Google Sheet spreadsheet.
Thanks
Take a look at the LockService.
For example in your code have:
var lock = LockService.getScriptLock();
if (lock.tryLock(1000)) { // Wait for 1s
// Do stuff ...
lock.releaseLock()
} else {
// This script is already running, try again later ...
}
I understood that when the script is run by clicking a button, you don't want to run the script more while the script is already running. If my understanding is correct, how about this workaround? Please think of this as one of several answers.
In this workaround, I selected to use CacheService. "CacheService allows you to access a cache for short term storage of data." The default expiration is 10 minutes. This is over the maximum execution time of GAS (6 minutes). So I selected this.
Flow :
Retrieve the key of "script" from CacheService.
If there is the key, it means that the script is running.
If there is no key, it means that the script is not running.
At this time, put the key of "script" with a value.
The process you want is run.
After the process was finished, the key is removed. This means the script is not running.
Sample script :
function failedSettlementsEmailz(){
var cache = CacheService.getScriptCache();
if (!cache.get("script")) {
cache.put("script", "running"); // you can use various value for the value of "running".
// do something
cache.remove("script");
}
}
Reference :
CacheService
If I misunderstand your question, I'm sorry.

Google Spreadsheet - Custom menu won't load because onOpen won't fire

I'm doing everything as prescribed in the 'Defining Spreadsheet Menus' tutorial. The menu is modified using the onOpen event handler.
I have made this 'Text to Columns' script available publicly in the 'Script Gallery' but I'm concerned that users who download may be confused when it takes a long time for the custom menu to pop up.
Getting the script to load the first time is proving to be a pain. Much of the time the onOpen trigger is missed altogether. It appears that the trigger isn't being set correctly because manually resetting the onOpen trigger will fix it.
For personal use I'd consider this a minor annoyance but for a shared script it becomes a support issue.
Note: Every subsequent load consistently takes about 7 seconds to appear which is OK but far from ideal.
Here's the onOpen handler:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [];
menuEntries.push({ name:"Text to columns", functionName:"textToColumns" });
menuEntries.push({ name:"Text to columns (custom separator)", functionName:"textToColumnsCustom" });
menuEntries.push(null);
menuEntries.push({ name:"Columns to Text", functionName:"columnsToText" });
menuEntries.push({ name:"Columns to Text (custom separator)", functionName:"columnsToTextCustom" });
ss.addMenu("Advanced", menuEntries);
}
Note: This was tested on a new (ie empty) spreadsheet with only one user.
i'm not sure but the user have to log in i guess, if not, i've got this problem once and solved it with making tow or more triggers all of them are on open and the same function,however try to get internet speed more than 30KB/s to make sure that the problem is from the code , not from your devices

function onOpen() is not running

My function includes adding a menu and toast to the document. I have verified that the trigger (onOpen) is set as well. It only works when a user goes into Tools, Script Manager, Run. We have too many users with too many backgrounds to expect then to know how to do this. Why isn't it working? (Using Chrome)
function onOpen()
{
var menus = [{name: "Advance in Workflow", functionName:"sendEmail"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu("Auto Advance FG Workflow", menus);
//sheet.toast(Notify/Remind users);
sheet.toast("While you are here we kindly ask that you do not add, modify or remove any columns.","Welcome - " + username,8);
}
Thanks,
I was having the same issue.
I realized, sometimes Google create some kind of cache of the scripts (I'm used to have a "test" script and I usually alter it's content, and, sometimes, the script runs as if I didn't).
So, what I did that solved the onOpen() not working was changing the function name and ading a trigger manually.
Go to "Resources -> Current script's triggers…"
Choose the function to run on open
It worked like a charm here!
Updated Location Information:
or
Then
This is an old post but I just had this problem and find out why it was not working correctly in my case:
I had, at the top of my script file a variable that required some authorisations and that prevented the script to correctly run. I saw that OP called var username = Session.getActiveUser().getUsername(); (that requires authorisations, and it's may be the cause).
eg:
this code won't work:
function onOpen(){
SpreadsheetApp.getUi()
.createMenu("Exportation")
.addItem("Lancer l'exportation", "exportationMenu")
.addToUi();
}
var stConsCons= SpreadsheetApp.openById(sgcid).getSheetByName("Consultant");
but this one will work:
function onOpen(){
SpreadsheetApp.getUi()
.createMenu("Exportation")
.addItem("Lancer l'exportation", "exportationMenu")
.addToUi();
}
function whatever(){
var stConsCons= SpreadsheetApp.openById(sgcid).getSheetByName("Consultant");
...}
It turns out that you need to add the onOpen(e) function to Triggers!
In my case, onOpen wasn't working because I had a variable, outside of a function, opening a sheet with SpreadsheetApp.openById() rather than SpreadsheetApp.getActiveSpreadsheet(). I guess onOpen doesn't work with openById() even if the sheet you are opening is bound to the script. onOpen() won't work with this kind of a variable outside of a function:
var sheet = SpreadsheetApp.openById("1b_PQD...").getSheetByName("demos")
If your script is bound to the sheet, you can solve this problem by using the getActiveSpreadsheet() function. Otherwise, you can solve it by putting your openById() call into a function.
In my case there was a reference error, that while did not stop the script entirely, it did stop the menu from appearing.
I was only able to detect that error after I run a debug on the script.
It looks like the problem may be that "sheet" isn't defined, which is why the toast is failing.
I know this is a really old question, but for any one finding this now, I may have a solution. The onOpen function often runs with the authorization mode none instead of limited when being used as an event trigger. This may cause errors in things that are related to the specific file or user data. For example:
function onOpen(e) {
SpreadsheetApp.getActivePresentation(); //Will error out if permissions are not set to limited.
SpreadsheetApp.getUi(); //This will always run even if the AuthMode is set to NONE
}
Additionally it is worth noting that if you have any variable used or initialized before onOpen(e), basically any global variables that access sensitive info fail if the AuthMode is set to NONE.
var ss = SpreadsheetApp.getActivePresentation(); //bad
var ss;
...
function init() {
ss = SpreadsheetApp.getActivePresentation(); //good because now that the function is already run we should have full permissions
}
Simple triggers silently fail if they lack permission. I ran into this with an onOpen() in a script that initialized File objects of non-bound files. I moved all File object instantiation to menu functions that do have permission.
In other words, this will not work
function onOpen(e) {
...
let file = DriveApp.getFileById(nonBoundFileId);
...
}
but this will work
function menuFunction() {
...
let file = DriveApp.getFileById(nonBoundFileId);
...
}
because menuFunction can be given permission(s) that simple triggers lack.