Question

This might be a newb question but.... Recently, I have been using window.setTimeout which makes a recursive call to the parent function, which makes an ajax poll to the server.

function connectToVM(portal) {
    //making the ajax call here....
   if(response.responseText !== "")
     {
    windowInterval = window.setTimeout(function() {
        connectToVM(portal)
    }
    , 4000);
   }
  }

windowInterval is my global var here.

if(!checkIfChartExists()) {
  window.clearInterval(windowInterval);
}

Now, instead of making use of variables here, I know that I can simple pass the function to clearTimeout, but that also causes all the other intervals to be stopped :(

The reason why I am doing this is the server does a timeout, only when there is a response. My scenario is, I have a chart which updates every timeout interval. AFAIK, when we set the interval, there is a specific value set to the variable(if set to a variable). So when I print my variable(every time when the timeout function is called), I get some int values which are unique.

I have many tabs, and many tabs can have same chart.. which just makes use of the same interval which is triggered earlier. Now I have just 1 chart.. But I have many charts to show which are of the same type. Say gauge Chart.

I also have to clear the timeout whenever there is no chart present in the current selected tab - which I am doing. So I am planning to make just 1 function which just makes the call to the server by passing in the required params to this function.

But in order to poll, I am using window.setTimeout thing I mentioned above. This works for 1 chart. Now, I try to add 1 more chart, with different set of params to poll the server, I will need to make use of some different setTimeout function, which has a id different than that of the earlier triggered timeout.

I also have to consider that if the 1st chart is already present, the timeout is already triggered and have to keep it running. So, now I have to trigger the second timeout. But there is no second timeout here.

I was wondering if there is any alternate approach to this, as I can't really predict how many chart's there will be on runtime.

Question 1 : Can we flood our browser with many timeout's?

Question 2 : How to get the id of that particular timeout, so that I can clearTimeout(id) on it?

Question 3 : Since we can't assign / make variables on the fly, how to set / make such a data structure which can hold such a pointer to the particular chart's index / id.. so that we can easily get hold of it and clear it.

Question 4 : Is this the only way we can poll the server(via AJAX) if we have to poll continually?

Lastly, I recognize this is a very complex issue I have posted in here. But I am sure I will find some useful information about the approach from the forums. I don't have much experience doing all these stuffs in JS, but any help is appreciated!


Update

Sorry I have to post my code in here.. But I am using Extjs to get my chart portlets. My code for the function connectToVM is this :

function connectToVM(portalId, host, port, user, passwd, db) {
      try{
        if(Ext.getCmp(portalId)) 
        {
            var gaugeChartForTitle = Ext.getCmp(portalId);

            if(typeof portalOriginalTitle === 'undefined')
                portalOriginalTitle = gaugeChartForTitle.title;

            var gaugeChartDiv = document.getElementById(portalId);

            Ext.Ajax.request({
                      url: "/connectToVM?" +
                            Ext.urlEncode({
                                host: host,
                                port: port,
                                user: user,
                                passwd: passwd,
                                db: db
                            }),
                      method: 'GET',
                      success: function (response, options) {

                          if(response.responseText !== "")
                          {
                              gaugeChartDiv.style.background = "";

                              gaugeChartForTitle.setTitle(portalOriginalTitle);

                              console.log("Virtual Machine at "+ host +" :  BUSY % : "+response.responseText);

                              virtualMachineStore.loadData(generateVirtualMachineData(response.responseText)); //Setting the data1 value of the store and loading it for display!

                              windowInterval = window.setTimeout(function() {
                                  connectToVM(portalId, host, port, user, passwd, db)
                                }
                              , 4000);
                          } 
                          else 
                          {
                              windowInterval = window.setTimeout(function() {
                                  connectToVM(portalId, host, port, user, passwd, db)
                                }
                              , 10000); //Retry every 10 seconds to check if the connection is established!

                              gaugeChartDiv.style.background = "red";
                              gaugeChartForTitle.setTitle(portalOriginalTitle +" - Connection Failure. Reconnecting!");
                          }
                      },
                      failure: function ( result, request) { 
                     }
              });
         }
             }
             catch(err) {
             }
         }

Now, I trigger my function using this :

function setWindowIntervalForVM(portalId) {
       //console.log("isIntervalActivated inside setWindowIntervalForVM() : "+isIntervalActivated);
       if(!isIntervalActivated) {
           connectToVM(portalId, host, port, user, pwd, db);
       }
    }

   function checkIfWindowIntervalIsActivated(portal) {
       if(!isIntervalActivated) {
            setWindowIntervalForVM(portal.id);
            isIntervalActivated = true;
        } else {
            window.clearInterval(windowInterval);
            windowInterval = null;
            isIntervalActivated = false;
        }
   }

So checkIfWindowIntervalIsActivated() is my parent function call which I call in these scenarios :

1) Whenever the Gauge Chart is newly created.. I Trigger this call and have a boolean isIntervalActivated which if it is false, triggers the server poll.

2) So now if I have the chart already in tab 1(since the user selected it), I now change to tab 2 which does not have it. So I simply set isIntervalActivated to true which stops the poll. This is handled for 1 chart. Now the question here is, if I want to make this function re-usable, say I want to drop one more chart of same type but with different server parameters to poll, how to make use of the same windowInterval variable which has my 1st chart's triggered timeout value. P.S: The value changes for every ajax request it makes. So there'z no 1 single value :(

3) I stop the poll whenever there is no chart of same type present.. in other tab. which makes perfect sense. Now, I am caching all my portlets whenever user drops in a new portlet / on the page load, pulling all the user configured portlets. In such a case, I have to trigger all of the charts' ajax calls.. each polling to its configured destination. Now, I do not know how many charts there will be, as in my function name, I am polling to VM's. So if the user consumes VM1, it switches to VM2 and so on.

So it's absolutely impossible to just create same function for many such similar charts. So just wanted to check if I can re-use the same timeOut thing, or take a totally different approach to this problem :( :(

I hope it's a bit clear now, if not I can explain my situation more. Please ask me more questions if required :) Thanks again!

Was it helpful?

Solution

If I understood correctly and you're trying to support multiple charts updating concurrently, I'd switch from keeping the chart data inside the connectToVM() closure to an explicit array of chart objects and use a single interval to update all charts.

Something like the following (treat it as pseudo-code):

var charts = [
  // an array of chart objects, see addChart()
];

function addChart() {
  // when you need to add or remove a chart, update the charts object, like this:
  charts.push({
    update: updateChart,
    nextUpdateTime: null, // or Date.now() if you don't care about old browsers.
    chartData: {host: ..., port: ..., user: ..., passwd: ..., db: ...,
                originalTitle: ..., portalId: ...},
  });
  restartUpdates();
}

var activeInterval = null;
function restartUpdates() {
  if (activeInterval) {
    clearInterval(activeInterval);
  }
  activeInterval = setInterval(updateCharts, 5000);
}

// updates all active charts
function updateCharts() {
  var now = new Date().getTime();
  for (var i = 0; i < charts.length; i++) {
    var chart = charts[i];
    if (chart.nextUpdateTime !== null && chart.nextUpdateTime < now) {
      chart.nextUpdateTime = null; // chart.update() will re-set this
      try {
        chart.update(chart);
      } catch(e) {
        // handle the error
      }
    }
  }

// update a single chart.
// @param |chart| is an item from the |charts| array.
function updateChart(chart) {
  // ...same as your connectToVM() using properties from chart.chartData...
  Ext.Ajax.request(
    // ...
      success: function (response, options) {
        // ...same as before...
        // ...but instead of re-setting the timeout:
        //   windowInterval = window.setTimeout(function() {
        //       connectToVM(portalId, host, port, user, passwd, db)
        //     }
        //   , 4000);
        // ...update the chart's nextUpdateTime:
        chart.nextUpdateTime = (new Date().getTime()) + 4000;
      }
  );
}

initial answer below

Thanks for the detailed question! It feels you're missing something very obvious wrt questions #2/3, but it's hard to tell what specifically without seeing more of your code. Can you post a more complete, yes simple example demonstrating the problem you're trying to solve? Perhaps the function handling changing the active tab in pseudocode would help, like this:

function selectTab(tabID) {
  // ...activate tab #tabID in the GUI...
  if (tabID == 1) {
    // there's chart #1 on tab #1, need to stop any active timeouts and start a new one 
    connectToVM("chart #1");
  } else if (tabID == 2) {
    // no charts on tab #2.. need to stop any active timeouts
  } else if (tabID == 3) {
    // ...
  }
}

One thing I don't understand is whether there's always a single chart, that needs updating, at any point of time?

Also, do you know the concepts mentioned in A re-introduction to JavaScript, specifically objects?

As for the questions:

  • 1: yes, too many timeouts should be avoided (thousands a second will probably make the CPU hot and the browser sluggish), although I'd be more worried about the server, which has to handle the requests from multiple clients.
  • 2/3: see above.
  • 4: The Comet page lists a lot of alternatives to basic AJAX polling (server-sent events, long-polling, websockets), but I'd worry about this later.

OTHER TIPS

  1. Yes
  2. var x = window.setTimeout(...); window.clearTimeout(x);
  3. Store it as a data attribute on the active tab, a property on your object, or as a global variable. Many different ways. Example code would have made it easier to answer this.

Based on your comments:

var windowInterval;
function connectToVM(portal) {
    if(windowInterval)window.clearTimeout(windowInterval);
    windowInterval = window.setTimeout(function() { ... }, 4000);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top