Match asynchronous response to original request

2606
7
08-16-2011 12:30 PM
DavidElies
New Contributor III
Hi all!

I have been trying to send dozens of queries to retrieve data points with different parameters.  I can't figure out how to tell which response came from which query.  The exact number of queries will be unknown, but it will most likely be 100+.  I can't see creating over 100 named functions to callback, and I can't figure out how to get external information into a shared callback or anonymous inline function.  As I loop through the array containing the data that will make up the queries, I have an index.  If I use  the index value within an inline function, by the time the callback function is called, the index is always max. 
for(var i = 0; i < queryArray.length; i++) {
   queryTask.execute(query, function(featureSet) {
      ...
      alert("Query Task #" + i);
   });
}

If queryArray.length = 25, I get 25 alert windows with "Query Task #24".

Does anybody know how to know which response corresponds to which request?

Thanks!
0 Kudos
7 Replies
StephenLead
Regular Contributor III
David,

John Grayson's response to a question of mine might give some pointers.

You may be able to use Deferred and its Fired properties to determine which response corresponds to which query, or to wait until the previous response has been received before you send the next query.

Steve
0 Kudos
DavidElies
New Contributor III
Thanks stevel,

I implemented your suggestion of waiting for one to complete before sending the next.  This actually works!  But the time it takes is prohibitive.  I can't figure out how using the returned Deferred would help me distinguish one from another.  A DeferredList seems like a good way to go, but since it waits until all Deferreds have fired, then fires itself, it still doesn't help me to tell which request corresponds to which deferred.  I think since the data returned doesn't indicate anything about the request, I'm outta luck.  Thanks for your help!
0 Kudos
derekswingley1
Frequent Contributor
The order of the results returned by a DeferredList are in the order in which your added Deferreds to the DeferredList. So, if you create a DeferredList like so:
var dl = new dojo.DeferredList([qt1, qt2, qt3]);
dl.addCallback(processQueryResults);

The first item in the array passed to the processQueryResults function will have the results from qt1 the second item in that array will be results from qt2, etc.

That being said, I still wouldn't encourage you to kick off dozens of queries...seems like a reliable way to slow down any app.
0 Kudos
DavidElies
New Contributor III
still wouldn't encourage you to kick off dozens of queries...seems like a reliable way to slow down any app.


You said it brother!  Have you any idea of how to return lots and lots of records reliably with ArcGIS Server 9.3.1?
0 Kudos
StephenLead
Regular Contributor III
I have been trying to send dozens of queries to retrieve data points with different parameters.


Can you provide more details about what the app is actually doing? Perhaps there's a way to streamline the backend processes - for example, running these queries on the server as a geoprocessing tool?

If it's unavoidable to run a series of long processes, it might be best to run them sequentially, and display an informative progress bar so people at least know what's going on.

EDIT: is this the same problem as http://forums.arcgis.com/threads/37327-I-need-to-visualize-100k-records ?

If so, some sort of geoprocessing model, which is run once on the server and returns a (possibly raster) result, might be one way to approach this

Cheers,
Steve
0 Kudos
derekswingley1
Frequent Contributor
You can modify your service to return more than the default 1000 records (the default was 500 at 9.3.1) but this is the default for a reason- increasing it can lead to slower response times.

Any chance you can re-consider your design? Because I'm curious, what are you hoping to do with thousands of records on the client? Could you possibly load them on demand instead?
0 Kudos
JohnGrayson
Esri Regular Contributor
I would agree with Derek and Steve; you need to be careful not to overwhelm the user experience with too much information. Keep it simple and only retrieve data from the server when necessary. Below is an example of how you can use dojo.Deferred and dojo.DeferredList to properly associate responses/results and the sources of the queries.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=7,IE=9" />
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
    <title>Get Counties By State</title>
    <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/2.4/js/dojo/dijit/themes/claro/claro.css">
    <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=2.4"></script>

    <script type="text/javascript">
      dojo.require("esri.map");
      dojo.require("esri.tasks.query");
      dojo.require("dojo.DeferredList");
      function init() {
        var map = new esri.Map("map");
        var imageParameters = new esri.layers.ImageParameters();
        imageParameters.format = "jpeg";
        var dynamicMapServiceLayer = new esri.layers.ArcGISDynamicMapServiceLayer("http://services.arcgisonline.com/ArcGIS/rest/services/Demographics/USA_Population_Density/MapServer", {
          "imageParameters": imageParameters
        });
        map.addLayer(dynamicMapServiceLayer);
        //
        // GET STATE FEATURES
        //
        getStates(dynamicMapServiceLayer, 4);
      }
      // GET STATE FEATURES
      function getStates(layer, subLayerIndex) {
        var statesQueryDeferred = doQuery(layer, subLayerIndex, '1=1')
        statesQueryDeferred.then(function(statesFeatueSet) {
          // BUILD LIST OF QUERY WHERE STRINGS
          var whereClauses = dojo.map(statesFeatueSet.features, function(statesFeature) {
            return {
              stateName: statesFeature.attributes.ST_ABBREV,
              where: dojo.replace("ST_ABBREV = '{attributes.ST_ABBREV}'", statesFeature)
            };
          });          
          getCountiesByState(layer, 3, whereClauses);
        });
      }
      //
      // GET COUNTY COUNT FOR EACH STATE
      //
      function getCountiesByState(layer, subLayerIndex, whereClauses) {
        // CREATE ARRAY OF DEFERREDS
        var countiesDeferredArray = dojo.map(whereClauses, function(whereClause) {
          // CREATE STATE NODE
          var stateNode = dojo.create('div', {
            'innerHTML': dojo.replace("{stateName} - ", whereClause)
          }, 'countyInfo');
          var countyCountNode = dojo.create('span', {
            'id': dojo.replace("countyCount_{stateName}", whereClause),
            'innerHTML': 'Searching...'
          }, stateNode);                              
          // RETURN QUERY DEFERRED
          return doQuery(layer, subLayerIndex, whereClause.where);
        }, this);
        // CREATE DEFERRED LIST FROM ARRAY OF DEFERREDS
        var countiesDeferredList = new dojo.DeferredList(countiesDeferredArray);
        countiesDeferredList.then(function(responses) {
          // WE GET ONE RESPONSE WHEN THEY'RE ALL DONE
          dojo.forEach(responses, function(response, responseIndex) {
            // FIND MATCHING WHERE CLAUSE
            var whereClause = whereClauses[responseIndex];            
            // FIND STATE NODE
            var stateNode = dojo.byId(dojo.replace("countyCount_{stateName}", whereClause));
            // DO WE HAVE A SUCCESSFUL RESPONSE
            if(response[0]) {
              var countyFeatureSet = response[1];
              // UPDATE STATE NODE WITH COUNTY COUNT
              stateNode.innerHTML = dojo.replace("{features.length} counties", countyFeatureSet);
            } else {
              stateNode.innerHTML = 'Error...';
            }
          }, this);
        });
      }
      //
      // QUERY
      //
      function doQuery(layer, subLayerIndex, where) {
        var query = new esri.tasks.Query();
        query.returnGeometry = false;
        query.outFields = ["*"]
        query.where = where;
        var queryTask = new esri.tasks.QueryTask(layer.url + "/" + subLayerIndex);
        return queryTask.execute(query);
      }
      dojo.addOnLoad(init);

    </script>
  </head>
  <body class="claro">
    <div id="map" style="width:500px; height:300px; border:1px solid #000;"></div>
    <ul id="countyInfo"></ul>
  </body>
</html>
0 Kudos