1

yes I'm new, and don't worry about making me sound dumb. :) This is a web app written in Google Script and it's taking info from Google Sheets.

I adopted a code from a comment here: Javascript timer to use multiple times in a page . Current code is below. I'm running several different timers thru this whole thing and they all go down by 1 second every second - great. My question is because the value on the google sheet that the first code grabs, is changing every few minutes. I WANT it to be counting down from the most recent data, and make the "old" counter STOP. From reading several related threads I think I need another clearInterval somewhere but I can't figure out where to put it.

Google Apps Script that grabs Google sheets values and puts them into a variable. the "getdata" values are dates in the future, and then "data" is how many seconds until that date:

    function getfivecoins(){
      var livesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Live");
      var getdata = livesheet.getRange("D42").getValue().getTime();
      var now = new Date().getTime();
      var data = (getdata-now)/1000; // how many seconds in the future this date is
      return data;
    }


function gettencoins(){
  var livesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Live");
  var getdata = livesheet.getRange("D43").getValue().getTime();
  var now = new Date().getTime();
  var data = (getdata-now)/1000;  // how many seconds in the future this date is
  return data;
}

then in html page:

<span id="timer1"></span>
    <br>
<span id="timer2"></span>
    <br>

lower down inside of script tags:

// every 10 seconds, gets new values from google sheets. Countdown should continue as normal (with some milliseconds difference, whatever) if values are the same, but trying to get countdown to start again if the value has changed. If its easier, clearing all the countdowns and then starting them again should have the same result.

  document.addEventListener('DOMContentLoaded', function(){
        setInterval(function(){
        google.script.run.withSuccessHandler(generatefivecoins).getfivecoins();
        google.script.run.withSuccessHandler(generatetencoins).gettencoins();
}, 10000);
        });


const clearStack = []; //Added
   function generatefivecoins(result) {
   clearStack.forEach(clearInterval); //Added
   var timer = document.getElementById('timer1');
   var t = new timerObject();
   clearStack.push(t.startTimer(result, timer)); //Modified
   }

    function generatetencoins(result){
    clearStack.forEach(clearInterval); //Added
    var timer =  document.getElementById("timer2");
    var t = new timerObject();
    clearStack.push(t.startTimer(result, timer)); //Modified
    } 





     var timerObject = function(){
    return this;
};

timerObject.prototype.startTimer = function(duration, display) {
  var me = this, 
      timer = duration,
      STYLETEST, hours, minutes, seconds, ENDSTYLETEST; 
var intervalLoop = setInterval(() => { // modified this line

    //fancy colours and formatting stuff goes here

  display.innerHTML = STYLETEST + " " + hours + ":" + minutes + ":" + seconds + ENDSTYLETEST;

    if (--timer < 0) {
      clearInterval(intervalLoop);
    }    
  }, 1000);
    return intervalLoop; //Added  
};
TheMaster
  • 45,448
  • 6
  • 62
  • 85
FattieQ
  • 13
  • 3
  • How about focusing on the minimal aspect of your [mcve]. – Cooper Apr 04 '20 at 05:08
  • You should really learn about [DRY](https://stackoverflow.com/a/56977915/) – TheMaster Apr 04 '20 at 07:56
  • Haha, I'm so scuffed I apologize. The DRY method is the goal but In Fattie land I thought "make it work first, then make it pretty" but really it makes sad walls of text when I look at it now. – FattieQ Apr 04 '20 at 08:08
  • On the server side,is 5, 10.... timers consecutively in range Column D:`D42:D43` and so on without gaps? ps To notify me, Address me as @TheMaster, when you reply. – TheMaster Apr 04 '20 at 08:17
  • @TheMaster They run from D42:D53 then beyond that they are elsewhere. I can always make a garbage sheet in google sheets though to have all of the 'sources' in one column if that helps troubleshoot though. – FattieQ Apr 04 '20 at 08:24

1 Answers1

0

Flow:

  • Instead of multiple functions, We use a single function with variable arguments.
  • coinBucket holds the different coins that can be generated
  • After generateCoins is called from server side, We use Map to store timerObject instance of class . During the next call, We use the same timerObject instead of creating a new one.

code.gs

function doGet(e) {
  return HtmlService.createHtmlOutputFromFile('index');
}

const getCoins = coin => {
  const map = { 5: 'D42', 10: 'D43', 20: 'D48', 60: 'D49', 90: 'D50' };//fivecoin range = D42 and so on
  var livesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Live');
  var getdata = livesheet
    .getRange(map[coin])
    .getValue()
    .getTime();
  var now = new Date().getTime();
  var data = getdata - now; // how many milliseconds in the future this date is
  return data;
};

Client side:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title></title>
  </head>
  <body>
    <h1>Coins!!</h1>
    <span id="timer1"></span>
    <span id="timer2"></span>
    <span id="timer3"></span>
    <span id="timer4"></span>
    <span id="timer5"></span>
  </body>
  <script>
    const coinBucket = [5, 10, 20, 60, 90];//number of coin buckets 
    const GSR = (func, ...args) =>// google.script.run to promise
      new Promise((resolve, reject) =>
        google.script.run
          .withSuccessHandler(resolve)
          .withFailureHandler(reject)
          [func](...args)
      );

    const clearStack = new Map();//map to store timerObjects
    function generateCoins(result, i) {
      const timer = clearStack.get(i) || new timerObject(i);
      clearStack.set(i, timer);
      timer.stopTimer();
      timer.startTimer(result);
    }
    class timerObject {
      constructor(id) {
        this.elementId = 'timer' + (id + 1);
        this.timerIds = [];
      }
      startTimer(duration) {
        const display = document.getElementById(this.elementId);
        this.timerIds.push(
          setInterval(() => {
            const hours = parseInt(duration / 1000 / 60 / 60, 10),
              minutes = parseInt((duration / 1000 / 60) % 60, 10),
              seconds = parseInt((duration / 1000) % 60, 10);
            display.innerHTML = [hours, minutes, seconds]
              .map((e) => ('0' + e).slice(-2))
              .join(':');
            duration = duration - 1000;
            if (duration < 0) this.stopTimer();
          }, 1000)
        );
      }
      stopTimer() {
        this.timerIds.forEach(clearInterval);
        this.timerIds = [];
      }
    }

    document.addEventListener('DOMContentLoaded', function () {
      setInterval(function () {
        Promise.all(
          coinBucket.map((bucket) => GSR('getCoins', bucket))
        ).then((values) => values.forEach(generateCoins));
      }, 10000);
    });
  </script>
</html>


Community
  • 1
  • 1
TheMaster
  • 45,448
  • 6
  • 62
  • 85
  • Thank you for the respone! I tried your submission here and it works great for the first timer, but only the first timer. I noticed the first line is outside of the function so I assume it doesn't need to be repeated but added the other lines to each of the "generate" functions and they load in one by one and no longer tick. Any ideas on how I found a way to goof that up? – FattieQ Apr 04 '20 at 06:49
  • @Fattie How many `generate` are there? – TheMaster Apr 04 '20 at 07:36
  • there are 37 of them altogether. I'm obsessed with numbers, apparently. – FattieQ Apr 04 '20 at 07:37
  • @Fattie Could you show me 5 instead of 2 in your question update? – TheMaster Apr 04 '20 at 07:40
  • 1
    goodness me. Thank you SO MUCH for all your work, it looks like you nearly had to rewrite all my mumbo jumbo. Can't express how happy I am to have this working!! – FattieQ Apr 05 '20 at 05:39
  • @Fattie Consider accepting the answer by clicking the checkbox on the left of my answer – TheMaster Apr 05 '20 at 06:44