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();
}
}
Related
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
}
I have a question / problem about a variable.
I have two page, in the first one I recover data and in the second one I do some operations.
ActivityPage.js (the first one)
recoverActivity() {
// this function check every second if the size of array > 1000
// this call only a function in the other page (Operations)
Operations.write({
arrayTimestamp: this.arrayTimestamp,
// other things
});
}
//this function when the user click a stop button.
stopActivity() {
Actions.Operations({
arrayTimestamp: this.arrayTimestamp,
});
}
And the I have another page
Operations.js:
//this is called from the first page directly
write(objectData) {
//...
this.timestampCheck(objectData.arrayTimestamp);
//...
}
//this is call from the ComponentDidMount of the second page.
stopClick() {
//...
this.timestampCheck(this.props.arrayTimestamp);
//...
}
Now my problem is in this timestampCheck function:
timestampCheck(timestamp) {
var int_max = 65536;
this.base = 0;
var diff = "";
var start = parseInt(this.contatore);
for (let i = 0; i < timestamp.length; i++) {
let timestamp = parseInt(timestamp[i]);
diff = (this.base + timestamp) - start;
if (diffDestro < 0) {
this.base+= int_max;
diff += this.base;
}
this.tempoReale.push(diff);
}
}
This function is called from the two function stopClick and write and there I have a variable this.base. Now I don't want that this variable loose his value when it leaves the functions timestampCheck. For example the arrayTimestamp has a size > 1000 an so it call the write() functions. here calculations are made and the value of this.base is set.
At this point, if the user clicks the stop key, the stopClick () function is called which calls the same timestampCheck function and must resume the previous value of this.base and not start from scratch.
How do you think I can do it?
thank you so much.
Just use a variable outside of the function to store the new value.
So outside of the function:
var countingValue = 0;
function timestampCheck(timestamp) {
var int_max = 65536;
this.base = 0;
var valueToUse = countingValue > 0 ? countingValue : this.base;
var diff = 0;
var start = parseInt(this.contatore);
for (let i = 0; i < timestamp.length; i++) {
let timestamp = parseInt(timestamp[i]);
diff = (valueToUse + timestamp) - start;
if (diffDestro < 0) {
valueToUse += int_max;
diff += valueToUse;
}
this.tempoReale.push(diff);
countingValue = countingValue + diff;
}
}
So what I have done here is create a variable outside of the function named countingValue with an initial value of 0.
Then underneath the initialisation of this.base I have used a type of If statement known as a ternary operator which says if the current countingValue is more than 0 then we will store that value in a variable named valueToUse otherwise we will use the this.base value and store it in the valueToUse variable.
In the rest of the code I have used the valueToUse variable for the computations now instead of this.base.
Note: I changed your variable diff to an integer because it was a string. You may want to review this and swap a couple of variables around if it's not exactly what you want.
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.
I am trying to make a script to pick random number between two numbers . but it picks same number sometimes. i donot want to repeat same number until array is finished .
Here is my code
$(document).ready(function () {
abc();
test = array();
function abc() {
res = randomXToY(1, 10, 0);
$('#img' + res).fadeTo(1200, 1);
//$(this).addClass('activeImg');
//});
setTimeout(function () {
removeClassImg(res)
}, 3000);
}
function removeClassImg(res) {
$('#img' + res).fadeTo(1200, 0.1);
//$('#img' + res).removeClass('activeImg');
abc();
}
function randomXToY(minVal, maxVal, floatVal) {
var randVal = minVal + (Math.random() * (maxVal - minVal));
return typeof floatVal == 'undefined' ? Math.round(randVal) : randVal.toFixed(floatVal);
}
});
Does Anybody have idea about this ...
You'll have to maintain a list of numbers that have already been generated, and check against this list. Re-generate a new number if you find a dupe.
If you do not want the random numbers repeating themselves you have to keep track of the some way.
If you have the range you are dealing with is relatively small, you can create an array with all possible results and simply randomly pick out of it.
function Randomizer(minVal, maxVal, floatVal){
var possible_results = []; // for larger arrays you can build this using a loop of course
var randomization_array = [];
var count = minVal;
var incrementor = floatVal || 1; // set the distance between possible values (if floatVal equals 0 we round to 1)
while (count <= maxVal) {
possible_results.push(count);
count += incrementor;
}
this.run = function(){
// if randomization_array is empty set posssible results into it
randomization_array = randomization_array.length ? randomization_array : $.merge(randomization_array, possible_results);
// pick a random element within the array
var rand = Math.floor(Math.random()*randomization_array.length);
// return the relevant element
return randomization_array.splice(rand,1)[0];
}
}
and in order to use it (it creates a specialized object for each possible range):
rand = new Randomizer(1,10,0);
rand.run();
note that this approach does not work well for very large ranges
I have a really simple JS counter which I display on a dashboard like screen which does the following:
Every 5 minutes it makes an jsonp call and retrieves a "total" number
It then displays this number to the screen by incrementing the last total displayed till it is equal to the new total. (the number can only ever increase)
I'm having some trouble with making the number increment smoothly. What I would like to do is find a delta (i.e. New total - old total) and increment the number gradually over the 5 minutes till the next call so it looks like a nice smooth transition.
Any ideas on how I can do this?
Currently some of my code looks like this (This block get's called every 5mins. And yes, it's in dire need of a refactor...)
var LAST_NUMBER_OF_SESSIONS = null;
var five_minutes_in_seconds = 300;
var new_number_of_sessions;
$.getJSON('http://blah.com/live_stats/default_jsonp.aspx?callback=?', function(data) {
if(LAST_NUMBER_OF_SESSIONS === null){
LAST_NUMBER_OF_SESSIONS = data.total_sessions;
}
new_number_of_sessions = data.total_sessions;
var delta = Math.floor(new_number_of_sessions - LAST_NUMBER_OF_SESSIONS);
var time_interval = (five_minutes_in_seconds / delta) * 1000;
var old_value = LAST_NUMBER_OF_SESSIONS;
var new_value = null;
sessions_interval = setInterval(function (){
new_value = parseInt(old_value, 10) + 1;
$('#stats').text(new_value);
old_value = new_value;
if(new_value >= new_number_of_sessions){
clearInterval(sessions_interval);
}
}, time_interval);
LAST_NUMBER_OF_SESSIONS = new_value;
});
}
This code it seems to increment the number very quickly at the start of the 5min period and then stop so it's not exactly right...
Try this:
var total = 0,
delta = 0,
stats = $('#stats').text( total );
function increment() {
var v = +stats.text();
if ( v < total ) {
stats.text( v + 1 );
} else {
$.getJSON('http://...', function(data) { // added data here
delta = Math.floor( 300000 / ( data.total_sessions - total ) );
total = data.total_sessions;
});
}
setTimeout(increment, delta);
}
Update:
In order to test my code, I had to simulate the JSON reponse - I used an array of numbers. See here: http://jsfiddle.net/simevidas/MwQKM/
(In the demo, I use an interval of 5 seconds instead of 5 minutes.)
I am not exactly sure why your code doesn't work as expected, although I suspect that it has to do with line LAST_NUMBER_OF_SESSIONS = new_value;. I wrote something similar and it works fine. It's not that different from what you have, minus that last line of code.