Allow the window to calculate before continuing Javascript (GAS) - javascript

I have seen a lot of duplicates on this subject, but I don't see how to actually get done what needs to be done.
I have a list of URLs in one sheet tab and an IMPORTXML() function in another. I'm writing a script to copy each URL to the second tab then perform an action based on the output of the IMPORTXML(). For this to work, I need a slight delay in the script to ensure the IMPORTXML() has calculated before continuing. setTimeout() doesn't seem appropriate here, because I need the other parameters of the script (which row it's checking, etc) to be calculated based on outputs. Help!
function test(){
var sh = SpreadsheetApp.getActiveSpreadsheet();
var list = sh.getSheetByName("Dec 2018").getRange(row,3,sh.getSheetByName("Dec 2018").getLastRow()-row).getValues();
var check = sh.getSheetByName("Check");
for(var row = 2;row<500;row++){
check.getRange(1,1).setValue(list[row-2][0]);
//wait right here
//other code to run based on the output of the =IMPORTXML() formula on the Check sheet
}
}

To insert a slight delay use Utilities.sleep(milliseconds) with a milliseconds value big enough to wait the slowest recalculation time (I think that it's 30000 ms for a single formula because it's the execution time limit for custom functions). If you want to optimize this time, maybe you will want to use a technique like exponential back-off
Note: The Window object isn't available on Google Apps Script server side code execution, so setTimeout() can't be used.

Related

converting synchronous code to asynchronous code

I am learning to develop chrome extensions(and at the same time learning javascript as well). I am experimenting with an extension that runs on youtube webpage. The thing is when I scroll, sometimes the webpage kinda feels a little jittery.
So, I started looking for possible reasons. I came across a few good videos including "what the heck is event loop by Philip Roberts" in which he talked about event loop and other related stuff. This made me wonder if it's because of this very reason the webpage feels jittery.
The code in the content script is synchronous and I don't have much experience with asynchronous coding in javascript. Concepts like promises, async/await are new to me.
Here is the code in the content script at a very high level.
chrome.storage.sync.get(); // getting some data here
let activateButton = document.createElement("div");
activateButton.id = "activate-ext";
activateButton.addEventListener("click", getData);
there is a call to chrome storage api
some code to render a button(there is a click event listener attached to this button which renders another UI.)
then I added this button to the dom
Now, most of the stuff is happening in the getData callback.
When a user clicks on that button. the extension:
retrieve data from the webpage and does some regex matching and then the string values that I want is stored in an array a.
then when I have the array a, I am using a map on it to create another array b
then another operation on all the elements of b to get another array c.
and now finally when I have the array c, I call another function that generates the final UI(using values from the array c)
This is how getData looks like:
function getdata(){
const regex = /some_regex/g;
let match = regex.exec(data);
while (match) {
a.push(match[index]);
match = regex.exec(description)
}
b = createAnotherArray(a) // this function returns array b
c = anotherOperation(b)
generateUI(c) // in this function I am rendering the UI
}
Now, all this code is synchronous(apart from the call to chrome storage API and some callbacks) and I believe some of these operations(like regex matching) take time which blocks the event loop.
So what would be the best way to restructure this whole code using promises(async/await) so it does not block the browser from rendering/updating UI.
I also saw some loops with async/await and I too have loops in my code too. So, do I need to do that too?
JavaScript is single-threaded. Event loop included - simply, it's just a queue for tasks put into it. If your JS code is actually causing performance issues doing some stuff asynchronously won't help, since then that code will block the event loop anyway.
Only available option is Web workers.

console.time shows different time running the same function

I use console.time to show the time of the function. But I found that it shows different running time of the same function.
I have simplified my function as below:
const findIP = (res) => {
let arr = []
arr = res.split(',')
}
console.time('1')
findIP('1,2,3,4,5,6,7,8,9,0')
console.timeEnd('1')
console.time('2')
findIP('1,2,3,4,5,6,7,8,9,0')
console.timeEnd('2')
The time difference between the two is very large.
I have tried to run several times. And it still cost different time.
To quote the answer in the the following link:
If you run shorten multiple times, the V8 engine has a JIT compiler that will optimize that piece of code so it runs faster the next time.
https://stackoverflow.com/a/54601440
Try changing the argument value, for example
console.time('1')
findIP('1,2,3,4,5,6,7,8,9,0')
console.timeEnd('1')
console.time('2')
findIP('1,2,3,4,43,6,7,8,9,4')
console.timeEnd('2')
you will see approx equal time
Reason of that difference is: The browser cache
Simple Definition
browser cache is a temporary storage area in memory or on disk that holds the most recently downloaded Web pages and/or calculated result.

How to measure User Interaction time of UI, menu made by Javascript?

I want to measure UI interaction time like following.
Time between Menu selected and showing result.
Time between User typing & showing letters in UI
I think many people suggest me to measure the time between function call & result, but I want to know another way to get the time though UI changes.
Is it possible to check the time for UI changes ?
The tool what I'm developing is made by Javascript and run on Browser.
Here is my answer:
"Javascript is single threaded".
So,what does this means:
Javascript runs code sequentially.It is not possible to run two
different pieces of Javascript code at the same time because it does
not support multithreading.
Execution of javascript code is line by line.So execution of multiple lines at same time is not possible(it will be very small though)
A simple solution:
check_my_time_first() ;//these will be functions returning the epoch time
check_my_time_after();
You just need console.log,flag variables and epoch time.
Firstly,the difference will be ultra small.
var milliseconds_before = (new Date).getTime();//use this code when user clicks
var milliseconds_after = (new Date).getTime();//use this when result appears.
Make use of flag variables to know when some execution has been completed.
Another example:
var execution_over=false;
function doSomething(){
console.log("start_time"+milliseconds_before)
//some code to be executed;
execution_over=true
if(execution_over==true){
console.log("time="+milliseconds_after)
}
}
Difference:
var diff=milliseconds_before - milliseconds_after;
Place this code smartly and you will see the difference in time.
The important thing is to understand the fundamentals.I hope my answer helped.

Javascript table on the fly

I've got some issues with javascript. Which causes some problems.
I'm using DevExpress MVC GridView, ASP.Net MVC 3 and javascript.
This my problem:
I've got a gridview, with for example customers.
I want them to select the customers, and show them in a table generated by javascript so we dont get all those refreshes. And they can then add other information so that they can be saved again to another table, but thats not really important.
I perform some calculations before generating the table row from the selected customer. Another problem is, the devexpress gridview has an event that calls on each selection change instead of a nice ~100 ms wait so that the user can multiselect quick without triggering method 3/4 times.
Im keeping track of my own table through an array. And the GridView from DevExpress got his own events that can give me the right information, so no need to worry about that.
So I got a method receiveSelectionFields(Values){ //do something } where I receive that information from the gridview on every selection.
Then I check my array to see if they added or removed a selection, and which.
Then I call addtablerow(customer) or removetablerow(customer). Which removes the customer from my table and then from my array.
Because I make some heavy calculations in between, there is a ~60ms delay before the calculation is done (on mine computer). So if the users makes 2 selections in 60 ms. My array will have the wrong value (not being modified by the first call that adds/removes a customer) and my javascript will cause an error e.g. the table row is not deleted. I check on length of my own array and on the length of the received array to see if something has been added or removed.
So what did I try?
Making my method a recursive method, that when the problem occurs it waites 60 ms and then redo the method. But this isn't working properly.
I tried adding a global variable busy, which is true when the method is still busy. And false when it ends. But my browser just quits when doing that. This was the code:
while (true) {
setTimeout(function () {
if (busy === false) {
break;
}
}, 50);
}
But I got the feeling it just endlessly loops.
And these are all workarounds, there must be a nice way to solve this. Any thoughts?
In short:
I want a way to let the functions go off in synch. even if their being called asynch. by the user so that my array doesn't mess up.
Found the answer why my problem exists:
Since javascript is a synch. language (1 thread). the functions should've triggered at the right time. The problem is the callback from DevExpress Gridview for MVC Extensions. It makes a callback to the server, which responds in for example ~150ms with the selected field values. This will give an error if you quickly trigger the devexpress function twice. The second trigger has a window to return FASTER then the first trigger. Which would mean my coding of the table get ruined since I check if something has been added or removed. So when the first trigger (which returns after the second trigger) and my table gets updated. It shows the table prior to my last selection. Thus missing 1 row or has 1 more row then it should've.
So I got to make a function that retrieves all the events, and then place them in an order ~200 ms after each order. To make sure there is enough time for the callback to retrieve. Though this is ofcourse still not reliable, I think I will just change the requirements on this.
Your while loop condition is true, therefore the loop will just continue endlessly. You may want to try the following:
var int = setInterval(function () {
if(busy === false) {
clearInterval(int);
}
}, 50);
Try this instead of looping through the setTimeout over and over. If I had to guess, the break is breaking the if statement but not the while loop causing your browser to get stuck in an endless loop. With the above code, you can set an interval at which to run the code. In this instance, the code runs every 50ms. Once the condition inside the if statement is true, the setInterval is cleared causing the browser to continue executing its normal functionality.
Hope this helps.

onChange for dropdown and timeouts

I have a dropdown menu which basically sends some data back to the server via onchange. The data is stored in the database and the primary table using seconds to avoid duplicates. What happens is if I scroll the dropdown via my keyboard, for example, using the up/down keys, my script returns an error because there is a duplicate entry now in the database.
here is part of the code for the dropdown in html
onchange="GetmySQLData();"
My question is can i just do a setTimeout and would it be correct to use it in this format:
onchange="setTimeout('GetmySQLData();', 1750);"
If you have a better approach, please feel free to let me know.
Thanks
The following versions are interchangeable:
setTimeout('myFunction()',1750);
setTimeout(myFunction,1750)
setTimeout(function() { /* some anonymous function */ },1750)
What you might want is
<script>
var tId = ""; // create a global var
</script>
and have
onchange="clearTimeout(tId); tId=setTimeout('GetmySQLData()',1750)"
which will only execute the onchange after the user does not change the dropdown for the duration of the timeout, but if the user changes within the timeout period, a new timeout is triggered
If you are planning to use the setTimeOut then it has to called in the following way:
onchange="setTimeout(GetmySQLData(), 1750);" providing single quotes aroung the arguments makes it to be treated as a string. and the timeout value is in milliseconds.

Categories

Resources