How to ignore Execution time of Google script and JS - javascript

I have for loop here for calling API calls from ticketmaster website and import in google sheets. The problem is I can't make many API calls at once I have to wait about 1 second.
So when I make the loop wait 1 second for example if the array has 40 element I wait 40 sec.
No problem with that due to the massive data but when the fucntion wait it gives an error of execution time limit, I need to set the execution time unlimited.
for (var Veunue_id1 = 0; Veunue_id1 < Venue_Id_List.length; Veunue_id1++) {
var Venue_API_Url = "https://app.ticketmaster.com/discovery/v2/venues?apikey=" + API_key + "&keyword=" + Venue_Id_List[Veunue_id1] + "&locale=*";
// ImportJSON(url, "/","noInherit,noTruncate,rawHeaders");
// console.log(ImportJSON(url, "/", "noInherit,noTruncate,rawHeaders"));
// console.log("Veuneid" + Veunue_id + Venue_Id_List.length);
results = results.concat(ImportJSON(Venue_API_Url, "/_embedded/venues/id", "noInherit,noTruncate,rawHeaders"));
console.log(results);
// wait1 second
Utilities.sleep(1000);
}

This a common issue, especially when working with heavy data API. General approach is:
Run as much as you can, until timeout(6 min) is close
Save loaded data
Store some indicator on there to resume on next run
On next run, repeat from position saved in 3)
Put this on trigger.
Example code based on your situation (not tested)
function SimpleTimer(timeout){
var start = Date.now();
this.getElapsed = () => Date.now() - start;
this.isTimeout = () => this.getElapsed() > timeout;
}
function resetProgress(){
PropertiesService.getScriptProperties().setProperty('last', '-1')
}
function test_SimpleTimer() {
var timer = new SimpleTimer(4*60*1000) // 4 min;
var start = PropertiesService.getScriptProperties().getProperty('last') || '-1';
for (var Veunue_id1 = parseInt(start) + 1; Veunue_id1 < Venue_Id_List.length && ! timer.isTimeout(); Veunue_id1++) {
var Venue_API_Url = "https://app.ticketmaster.com/discovery/v2/venues?apikey=" + API_key + "&keyword=" + Venue_Id_List[Veunue_id1] + "&locale=*";
results = results.concat(ImportJSON(Venue_API_Url, "/_embedded/venues/id", "noInherit,noTruncate,rawHeaders"));
Utilities.sleep(1000);
PropertiesService.getScriptProperties().setProperty('last', '' + Veunue_id1)
}
// SAVE YOUR DATA HERE, NEXT CALL WILL PROCEED FROM LAST STEP
}

Related

How to run function unknown amount of times based on variable?

I'm trying to create a Javascript API function which retrieves data from the endpoint. The problem here is it returns a maximal amount of 1000 records and the total record amount can be up to 300.000 records. So I need to loop this API function and constantly add '1000' to the start variable every time it runs. So this way the function will keep getting records until it hits the unknown total amount of records. Let's say this example has 300.000. Then it should loop 300 times.
The API and everything work perfectly. The only I problem I have the start variable doesn't add 1000 to itself after every time it has run. It should add 1000 to the variable itself and then run again, to retrieve the next 1000 records.
This is my function:
function loopFunction() {
function getResponse() {
if (typeof counter === undefined || counter === null) {
var start = 0;
}
var response = UrlFetchApp.fetch("https://www.apiurl?start=" + start + "&limit=1000&access_token=xxx");
// Parse data to JSON and get total
var fact = response.getContentText();
var data = JSON.parse(fact);
// Retrieve start number and count
var start = data.start += 1000;
var count = data.count;
var runAgain = count + start;
var counter = 1;
return runAgain;
}
var runAgain = getResponse();
console.log(runAgain);
loopFunction();
}
}
When I run this function the data is retrieved and the looping part works. The only problem is the console.log(runAgain); keeps returning 1000 instead of 1000, 2000, 3000, 4000, 5000 etc. Does anyone know how to fix this?
You need to declare start/counter outside the function as global. And use the same variable inside and update start/counter as per. Also add a condition checking data.count to exit the loop if it's the last or unknown amount is reached
var start;
var counter;
function loopFunction() {
function getResponse() {
if (counter === 'undefined' || counter === null) {
start = 0;
}
var response = UrlFetchApp.fetch("https://www.apiurl?start=" + start + "&limit=1000&access_token=xxx");
// Parse data to JSON and get total
var fact = response.getContentText();
var data = JSON.parse(fact);
// Retrieve start number and count
start += 1000;
var count = data.count;
var currentStart = data.start;
var runAgain = count + currentStart;
counter = 1;
return runAgain;
}
var runAgain = getResponse();
console.log(runAgain);
loopFunction();
}
}

The output from SetTimeout function is inconsistent

I am trying to create something like a Grid-Image-Box-Slider. Now what I am trying to achieve is that after a period of time (e.g 5 seconds) one of the images will be changed randomly. So, my script follows:
var currentImages = [1, 2];
(function imgCarousel() {
var min = 1;
var max = 6;
currentImgSlot = pickImageSlot();
var pickedImage = pickImage();
setTimeout(function () {
console.log("Image Slot: " + currentImgSlot + "<br> Image: " + pickedImage);
//var bgImgElem = document.getElementsByClassName('bg1');
var sheet = new CSSStyleSheet();
sheet.replaceSync('.bg' + currentImgSlot + ' {background-image: url("assets/img/' + pickedImage + '.jpg") !important}');
document.adoptedStyleSheets = [sheet];
return imgCarousel();
}, 5000)
})()
function pickImageSlot() {
var min = 1;
var max = 2;
var generatedImgSlot = generateRandomNumber(max, min);
if (generatedImgSlot == currentImgSlot) {
return pickImageSlot();
}
return generatedImgSlot;
}
function pickImage() {
var min = 1;
var max = 6;
var generatedImg = generateRandomNumber(max, min);
if (currentImages[currentImgSlot] == generatedImg) {
return pickImage();
}
currentImages[currentImgSlot] = generatedImg;
return generatedImg;
}
function generateRandomNumber(max, min) {
return Math.round(Math.random() * (max - min) + min)
}
So, here you can see that inside the imgCarousel() function I am using the setTimeout to change the background-image randomly after 5 seconds and then calling the same function again recursively. So, according to this code after each five seconds only one image should be changed. But, in reality, sometimes both of the images get changed at the same time. I don't know what is causing this issue.
Any help would be much appreciated. For your convenience I am sharing my code repo here:
Git Repo
Live Demo
It's a bit difficult to notice, but if you pay attention, in your live demo you'll actually see that each time they both change, one returns to the default. This is because you're actually just overriding the adoptedStyleSheets property with a new array consisting only of the latest override, so your other image will simultaneously return to the primary style sheet's image (the default).
There is no problem with the setTimeout, the issue is just that you need to be able to support both images being different from the default, instead of exactly one. You already keep both images in currentImages, so each time the timeout runs you can iterate over them and set each one's image in the new adopted style sheet.
Naively, something like this might work:
setTimeout(function () {
console.log("Image Slot: " + currentImgSlot + "<br> Image: " + pickedImage);
//var bgImgElem = document.getElementsByClassName('bg1');
var sheet = new CSSStyleSheet();
for(var i = 1; i < 2; i++) {
sheet.replaceSync('.bg' + i + ' {background-image: url("assets/img/' + currentImages[i]+ '.jpg") !important}');
}
document.adoptedStyleSheets = [sheet];
return imgCarousel();
}, 5000)
Your currentImages seems to be used with a random number between 1 and 2, but arrays are 0-based, so I'm not sure what your intended usage is there, but this should get you on the right track.

For Loop running infinitely

So Ive been fighting with this for loop for a day and a half now, when I get it to actually print it goes infinitely, even when the if statement (logCount === 10) is satisfied...
Not too sure what to try anymore, really feel like its far simpler than what I'm trying....
Any attempts at a solution are appreciated..
var timers = [];
var log = (function(outputFunc) {
var counter = 0;
var callerLog = [];
var dateTime = [];
//assigning current Date to a variable to print for logs
let logDate = new Date();
return function(timer) {
for (var logCount = 0; logCount <= 10; logCount++) {
//printing data without using printFunc, as specified
document.getElementById("output").innerHTML += logCount + " " + timer + " " + logDate + "<br>";
//TODO: add after for loop is resolved.
if (logCount >= 10) {
clearInterval(timer1);
clearInterval(timer2);
clearInterval(timer3);
document.getElementById("output").innerHTML += "<br><br/> Logging stopped.";
}
}
}
})(printFunc);
function printFunc(output) {
document.write(output + "<br>");
}
function startMeUp() {
// add each of the timer references to the timers array
// as part of invoking the log function following each interval
timers.push(setInterval("log('Timer1')", 1000));
timers.push(setInterval("log('Timer2')", 1200));
timers.push(setInterval("log('Timer3')", 1700));
}
I'm guessing this is what you're trying to achieve:
function printFunc(output) {
// Replaced with console.log for my own convenience
console.log(output);
}
// Not really a factory, just a curry of the outputFunc
function loggerFactory(outputFunc) {
return function startLogger(name, interval) {
// Variables to keep track of the iterations and a reference to the interval to cancel
let logCount = 0;
let intervalRef;
function tick() {
// On each tick, check if we're done
// If yes, clear the interval and do the last output
// If no, do some output and increment the iterator
// Once the next tick passes we'll check again
// If you were using setTimeout instead you would have to requeue the tick here
if (logCount >= 10) {
clearInterval(intervalRef);
outputFunc('Done ' + name);
} else {
outputFunc(logCount + " " + name);
logCount += 1;
}
}
// Start it of
intervalRef = setInterval(tick, interval);
}
}
const logger = loggerFactory(printFunc);
function startMeUp() {
console.log('Starting');
logger('Log1', 1000);
logger('Log2', 1200);
logger('Log3', 1700);
}
startMeUp();
Some notes:
You could push the intervalRefs into an array but I find it nicer to encapsulate that work within the same logger, since it should only clean up itself anyway.
When working with intervals (or asynchronous code in general) for loops are usually not what you're looking for. For loops are inherently synchronous, all iterations will be run directly after each other, without space for anything else. What you were looking for was a way to run multiple asynchronous "tracks" at the same time. You could use a for loop to start these "tracks", such as:
for () {
logger('Log' + i, 1000 * i);
}
But the key lies in that the logger quickly sets up the interval function and then returns. That way your for loop can quickly "schedule" the tasks but the logger runs the iterations internally asynchronously by using setInterval or setTimeout.

Determining time remaining until bus departs

For our digital signage system, I'd like to show how long until the next bus departs. I've built the array that holds all the times and successfully (maybe not elegantly or efficiently) gotten it to change all that to show how much time is remaining (positive or negative) until each listed departure.
I need a nudge in the right direction as to how to determine which bus is next based on the current time. If there is a bus in 7 minutes, I only need to display that one, not the next one that leaves in 20 minutes.
I was thinking perhaps a for loop that looks at the array of remaining times and stops the first time it gets to a positive value. I'm concerned that may cause issues that I'm not considering.
Any assistance would be greatly appreciated.
UPDATE: Unfortunately, all the solutions provided were throwing errors on our signage system. I suspect it is running some limited version of Javascript, but thats beyond me. However, the different solutions were extremely helpful just in getting me to think of another approach. I think I've finally come on one, as this seems to be working. I'm going to let it run over the holiday and check it on Monday. Thanks again!
var shuttleOrange = ["09:01", "09:37", "10:03", "10:29", "10:55", "11:21", "11:47", "12:13", "12:39", "13:05", "13:31", "13:57", "14:23", "14:49", "15:25", "15:51", "16:17", "16:57", "17:37", "18:17"];
var hFirst = shuttleOrange[0].slice(0,2);
var mFirst = shuttleOrange[0].slice(3,5);
var hLast = shuttleOrange[shuttleOrange.length-1].slice(0,2);
var mLast = shuttleOrange[shuttleOrange.length-1].slice(3,5);
var theTime = new Date();
var runFirst = new Date();
var runLast = new Date();
runFirst.setHours(hFirst,mFirst,0);
runLast.setHours(hLast,mLast,0);
if ((runFirst - theTime) >= (30*60*1000)) {
return "The first Orange Shuttle will depart PCN at " + shuttleOrange[0] + "."
} else if (theTime >= runLast) {
return "Orange Shuttle Service has ended for the day."
} else {
for(var i=0, l=shuttleOrange.length; i<l; i++)
{
var h = shuttleOrange[i].slice(0,2);
var m = shuttleOrange[i].slice(3,5);
var departPCN = new Date();
departPCN.setHours(h,m,0);
shuttleOrange[i] = departPCN;
}
for(var i=shuttleOrange.length-1; i--;)
{
//var theTime = new Date();
if (shuttleOrange[i] < theTime) shuttleOrange.splice(i,1)
}
var timeRem = Math.floor((shuttleOrange[0] - theTime)/1000/60);
if (timeRem >= 2) {
return "Departing in " + timeRem + " minutes."
} else if (timeRem > 0 && timeRem < 2) {
return "Departing in " + timeRem + " minute."
} else {
return "Departing now."
}
}
You only need to search once to find the index of the next scheduled time. Then as each time elapses, increment the index to get the next time. Once you're at the end of the array, start again.
A sample is below, most code is setup and helpers. It creates a dummy schedule for every two minutes from 5 minutes ago, then updates the message. Of course you can get a lot more sophisticated, e.g. show a warning when it's in the last few minutes, etc. But this shows the general idea.
window.addEventListener('DOMContentLoaded', function() {
// Return time formatted as HH:mm
function getHHmm(d) {
return `${('0'+d.getHours()).slice(-2)}:${('0'+d.getMinutes()).slice(-2)}`;
}
var sched = ["09:01", "09:37", "10:03", "10:29", "10:55", "11:21", "11:47",
"12:13", "12:39", "13:05", "13:31", "13:57", "14:23", "14:49",
"15:25", "15:51", "16:17", "16:57", "17:37", "18:17","21:09"];
var msg = '';
var msgEl = document.getElementById('alertInfo');
var time = getHHmm(new Date());
var index = 0;
// Set index to next scheduled time, stop if reach end of schedule
while (time.localeCompare(sched[index]) > 0 && index < sched.length) {
++index;
}
function showNextBus(){
var time = getHHmm(new Date());
var schedTime;
// If run out of times, next scheduled time must be the first one tomorrow
if (index == sched.length && time.localeCompare(sched[index - 1]) > 0) {
msg = `Current time: ${time} - Next bus: ${sched[0]} tomorrow`;
// Otherwise, show next scheduled time today
} else {
// Fix index if rolled over a day
index = index % sched.length;
schedTime = sched[index];
msg = `Current time: ${time} - Next bus: ${schedTime}`;
if (schedTime == time) msg += ' DEPARTING!!';
// Increment index if gone past this scheduled time
index += time.localeCompare(schedTime) > 0? 1 : 0;
}
msgEl.textContent = msg;
// Update message each second
// The could be smarter, using setInterval to schedule running at say 95%
// of the time to the next sched time, but never more than twice a second
setInterval(showNextBus, 1000);
}
showNextBus();
}, false);
<div id="alertInfo"></div>
Edit
You're right, I didn't allow for the case where the current time is after all the scheduled times on the first running. Fixed. I also changed all the string comparisons to use localeCompare, which I think is more robust. Hopefully the comments are sufficient.
I have used filter for all shuttle left after the right time and calculated how much time left for the first one.
var shuttleOrange = ["09:01", "09:37", "10:03", "10:29", "10:55", "11:21", "11:47", "12:13", "12:39", "13:05", "13:31", "13:57", "14:23", "14:49", "15:25", "15:51", "16:17", "16:57", "17:37", "18:17"];
var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
var remainShuttle = shuttleOrange.filter(bus => bus.substring(0,2) > h || (bus.substring(0,2) == h && bus.substring(3,5) > m));
var leftMinutes = (parseInt(remainShuttle[0].substring(0,2))*60 + parseInt(remainShuttle[0].substring(3,5)) - (parseInt(h) *60 + parseInt(m)));
console.log(parseInt(leftMinutes / 60) + " hours and " + leftMinutes % 60 +" minutes left for next shuttle");

Async python script from node js

I have a node.js program that runs around 50 different python script instances. I would like to be able to throttle the phase - such that at any one time, only 4 processes will run parallel.
I tried a simple loop that calls a function that runs 4 instances of the python script. I used 60 seconds delay as this is the average time takes the script to run.
function startPythonScraping(){
for(var i = 0; i < finalList.length; i++){
setTimeout(function(){
runFourProccesses(finalList[i]);
}, i*60*1000);
}
}
function runFourProccesses(stockSymbol){
console.log("working on " + stockSymbol);
for(var j = 0; j < 4; j++){
if(j == 0){
trueOrFalse = "y"
} else {
trueOrFalse = "n"
}
let secondPythonProcess = spawn('python', ["/Users/nybgwrn/Desktop/AlphaSecWebsite/getAllData.py", stockSymbol, (j*10).toString(), "10", trueOrFalse]);
secondPythonProcess.stdout.on('data', (data) => {
let messegeFromPython = JSON.stringify(data.toString('utf8')).replace("\\n", "");
console.log(messegeFromPython + " with stock " + stockSymbol);
if(messegeFromPython != "something went wrong"){
//console.log("created file for " + symbol);
} else {
//console.log("couldn't create file for " + symbol + "_" + (j*10).toString() + "-" + (j*10 + 10).toString());
}
});
}
}
It doesn't work because somehow the index i begin with 50 instead of 0, and also I want a better solution as I want to be SURE that only 4 instances are running.
This is usually done using semaphores. A semaphore is basically a pool of locks, so that multiple locks can be held at the same time.
In your case you could use this package and wrap each python process spawning with sem.take and sem.leave.

Categories

Resources