QueryTask.execute issue

1070
5
Jump to solution
05-15-2017 07:33 AM
SaraEL_MALKI
Occasional Contributor II

Hi everyone,

I am facing an issue that I can't solve so far,

I am using a QueryFeatures function which returns many items , for EACH item of the result I wanna do a QUERY and repeat it UNTIL IT IS SUCCESSUL, 

my problems are: - the query still loops after being successful

                             -the order of the functions executed

this code displays me First all the ALERTS of the while as example:

(id troncon 1) (id troncon 5) (id troncon 8) When the length is satisfied then it starts printing me alert EXECUTING, EXECUTING EXECUTING

INSTEAD it must print me id troncon 1 then EXECUTING, id trondon 5 EXECUTING id troncon 8 EXECUTING

I hope that I am clear and I hope you can help me as soon as possible 

 

FLayer2 = map.getLayer("troncon");

FLayer2.setSelectionSymbol(symbolLine1);
FLayer2.selectFeatures(selectionQuery,esri.layers.FeatureLayer.SELECTION_NEW);
FLayer2.on("selection-complete", zoomToVoie);
FLayer2.queryFeatures(selectionQuery, selectInVoie);
}
function selectInVoie(response){
var feature;
var features = response.features;
var queryTaskAdr = new QueryTask("....../1"); //ADRESSES
var idTroncon;
var found=false;
//SELECTION OF THE DOT
var line = new SimpleLineSymbol();
line.setWidth(3.25);
var marker = new SimpleMarkerSymbol();
marker.setOutline(line);
marker.setColor(new Color([255, 0, 0, 1])); // A RED DOT
marker.setAngle(360);

var i=0;
while( (i < features.length) && (!found) ){  //REPEAT THE WHILE UNTIL THE variable FOUND=TRUE
feature = features[i];
idTroncon=feature.attributes[FLayer2.objectIdField];
alert("idTroncon "+idTroncon);
var queryAdr = new Query();
queryAdr.where = "Text_ = '"+TheAdr+"' AND ID_TRONCON ="+idTroncon;
queryAdr.outSpatialReference = {wkid:102100};
queryAdr.returnGeometry = true;
queryAdr.outFields = ["objectid"];

queryTaskAdr.execute(queryAdr, function(fset) {
alert(' EXECUTING');
if (fset.features.length === 1) { //POINT FOUND  //BREAK
var objectidOfAdr=fset.features[0].attributes['objectid']; 
var geoAdr=fset.features[0].geometry

var FLayer1 = map.getLayer("adresse"); 
FLayer1.setSelectionSymbol(marker);
var selectionQuery1 = new Query();
selectionQuery1.where = "objectid ="+objectidOfAdr;
FLayer1.selectFeatures(selectionQuery1,esri.layers.FeatureLayer.SELECTION_NEW);

ExtentAdr=new Extent(geoAdr.x, geoAdr.y, (geoAdr.x)+50, (geoAdr.y)+50, geoAdr.spatialReference);
FLayer1.on("selection-complete", zoomToAdr);
found=true;
}

});

i++; //INCREMENT
}
}

function zoomToVoie(){
map.setExtent(ExtentOfVoie);
}
}
0 Kudos
1 Solution

Accepted Solutions
ThomasSolow
Regular Contributor

The issue is that all the requests (inside the while loop) are fired off simultaneously (or within a few milliseconds of each other).  So every request is sent before a single response is received.

Ken's solution is correct in my opinion.  It's generally preferable to send all the requests you need and in the callback function look for a certain condition to be met.  This will result in a better user experience (faster) and will waste less time.

If you don't want to do things this way (although I think it's the right choice), you have two main alternatives.  One option is to execute the requests one at a time, end to end.  So you'll send a query for the first feature in the array, then wait for the result, check the result, if you haven't found what you're looking for then you can send the query for the next item.  Here's an example of how you might do this:

var TheAdr; // string 
var features = [...] // array of features

doQueryRecursive(features, 0, TheAdr);

// takes the list  of features, the index, and the address
function doQueryRecursive(features, idx, TheAdr){
  var feature = features[idx]; // set the feature
  idTroncon=feature.attributes[FLayer2.objectIdField];
  alert("idTroncon "+idTroncon);

  // construct the query
  var queryAdr = new Query();
  queryAdr.where = "Text_ = '"+TheAdr+"' AND ID_TRONCON ="+idTroncon;
  queryAdr.outSpatialReference = {wkid:102100};
  queryAdr.returnGeometry = true;
  queryAdr.outFields = ["objectid"];

  // submit query
  queryTaskAdr.execute(queryAdr, function(fset) {
    alert(' EXECUTING'); // you labeled this executing, but this function
                         // is actually called when the query is finished.
    
    if (fset.features.length === 1) { // check for point, if point is found 
                                      // do what needs to be done

      var objectidOfAdr=fset.features[0].attributes['objectid']; 
      var geoAdr=fset.features[0].geometry; 

      var FLayer1 = map.getLayer("adresse"); 
      FLayer1.setSelectionSymbol(marker);
      var selectionQuery1 = new Query();
      selectionQuery1.where = "objectid ="+objectidOfAdr;
      FLayer1.selectFeatures(selectionQuery1,esri.layers.FeatureLayer.SELECTION_NEW);

      ExtentAdr=new Extent(geoAdr.x, geoAdr.y, (geoAdr.x)+50, (geoAdr.y)+50, geoAdr.spatialReference);
      FLayer1.on("selection-complete", zoomToAdr);
    } else { // if point is not found...
      if (features[idx + 1]){ // check if there is another feature
        doQueryRecursive(features, idx + 1, TheAdr); // if there is another feature
                                                     // submit a query for the next feature
      } else {
        // if there is no other feature, then give feedback
        alert("No matches found");
      }
    }
  }.bind(this));
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

Another approach would be to send all requests at the same time (like you're doing now) and then take a look at all the responses once every requests has returned.  You can do this with Promise.all:

var TheAdr; // string 
var features = [...] // array of features

var promises = mapToPromises(features, TheAdr);

// wait for all requests to be fulfilled, then check the responses
Promise.all(promises).then(function(results){
  var foundResult = false;
  for (var i=0; i<results.length; i++){
    if (results[i].features.length === 1) {

      var objectidOfAdr=fset.features[0].attributes['objectid']; 
      var geoAdr=fset.features[0].geometry; 

      var FLayer1 = map.getLayer("adresse"); 
      FLayer1.setSelectionSymbol(marker);
      var selectionQuery1 = new Query();
      selectionQuery1.where = "objectid ="+objectidOfAdr;
      FLayer1.selectFeatures(selectionQuery1,esri.layers.FeatureLayer.SELECTION_NEW);

      ExtentAdr=new Extent(geoAdr.x, geoAdr.y, (geoAdr.x)+50, (geoAdr.y)+50, geoAdr.spatialReference);
      FLayer1.on("selection-complete", zoomToAdr);
      foundResult = true;
      break;
    }
  }

  if (!foundResult){
    alert("Didn't find any results");
  }
});

// takes the list  of features and the address, returns an 
// array of promises
function mapToPromises(features, TheAdr){
  return features.map(feature, function(){
    // construct the query
    var queryAdr = new Query();
    queryAdr.where = "Text_ = '"+TheAdr+"' AND ID_TRONCON ="+idTroncon;
    queryAdr.outSpatialReference = {wkid:102100};
    queryAdr.returnGeometry = true;
    queryAdr.outFields = ["objectid"];
    // submit the query, retuen the promise;
    return queryTaskAdr.execute(queryAdr);
  });
}

My advice would be to use Ken's approach though.

View solution in original post

5 Replies
KenBuja
MVP Honored Contributor

The problem you're encountering is that the basic loop you're running fires off a query task but doesn't wait for it to be completed before looping again and firing the next query task. Instead of checking for "found" in the while loop, why not move that into the query itself. The queries will still run, but on the first time you find just one feature, the extent is set. Any other queries with one feature won't run that subroutine.

queryTaskAdr.execute(queryAdr, function(fset) {
  alert(' EXECUTING');
  if (fset.features.length === 1 && !found) { //POINT FOUND  //BREAK
    var objectidOfAdr=fset.features[0].attributes['objectid']; 
    var geoAdr=fset.features[0].geometry; 

    var FLayer1 = map.getLayer("adresse"); 
    FLayer1.setSelectionSymbol(marker);
    var selectionQuery1 = new Query();
    selectionQuery1.where = "objectid ="+objectidOfAdr;
    FLayer1.selectFeatures(selectionQuery1,esri.layers.FeatureLayer.SELECTION_NEW);

    ExtentAdr=new Extent(geoAdr.x, geoAdr.y, (geoAdr.x)+50, (geoAdr.y)+50, geoAdr.spatialReference);
    FLayer1.on("selection-complete", zoomToAdr);
    found=true;
  }
});
SaraEL_MALKI
Occasional Contributor II

Thanks Ken four your answer but this is not my problem at all, already for me just one time the condition "fset.features.length===1" is satisfied so I do't need to add another condition (!found), instead my problem is I don't wanna the code inside the while to run anymore after finding a result which means 'fset.features.length===1', that means I don't want it to create an object Query nor to execute any queryTask and to break from the while.

0 Kudos
SaraEL_MALKI
Occasional Contributor II

my mean trouble is when I initialize the Query object, the QueryTask is not executed immediately after that because the alert 'Executing' is not showing at the perfect time , do I need to add another function to check if the query is ready to be executed or what ?

0 Kudos
ThomasSolow
Regular Contributor

The issue is that all the requests (inside the while loop) are fired off simultaneously (or within a few milliseconds of each other).  So every request is sent before a single response is received.

Ken's solution is correct in my opinion.  It's generally preferable to send all the requests you need and in the callback function look for a certain condition to be met.  This will result in a better user experience (faster) and will waste less time.

If you don't want to do things this way (although I think it's the right choice), you have two main alternatives.  One option is to execute the requests one at a time, end to end.  So you'll send a query for the first feature in the array, then wait for the result, check the result, if you haven't found what you're looking for then you can send the query for the next item.  Here's an example of how you might do this:

var TheAdr; // string 
var features = [...] // array of features

doQueryRecursive(features, 0, TheAdr);

// takes the list  of features, the index, and the address
function doQueryRecursive(features, idx, TheAdr){
  var feature = features[idx]; // set the feature
  idTroncon=feature.attributes[FLayer2.objectIdField];
  alert("idTroncon "+idTroncon);

  // construct the query
  var queryAdr = new Query();
  queryAdr.where = "Text_ = '"+TheAdr+"' AND ID_TRONCON ="+idTroncon;
  queryAdr.outSpatialReference = {wkid:102100};
  queryAdr.returnGeometry = true;
  queryAdr.outFields = ["objectid"];

  // submit query
  queryTaskAdr.execute(queryAdr, function(fset) {
    alert(' EXECUTING'); // you labeled this executing, but this function
                         // is actually called when the query is finished.
    
    if (fset.features.length === 1) { // check for point, if point is found 
                                      // do what needs to be done

      var objectidOfAdr=fset.features[0].attributes['objectid']; 
      var geoAdr=fset.features[0].geometry; 

      var FLayer1 = map.getLayer("adresse"); 
      FLayer1.setSelectionSymbol(marker);
      var selectionQuery1 = new Query();
      selectionQuery1.where = "objectid ="+objectidOfAdr;
      FLayer1.selectFeatures(selectionQuery1,esri.layers.FeatureLayer.SELECTION_NEW);

      ExtentAdr=new Extent(geoAdr.x, geoAdr.y, (geoAdr.x)+50, (geoAdr.y)+50, geoAdr.spatialReference);
      FLayer1.on("selection-complete", zoomToAdr);
    } else { // if point is not found...
      if (features[idx + 1]){ // check if there is another feature
        doQueryRecursive(features, idx + 1, TheAdr); // if there is another feature
                                                     // submit a query for the next feature
      } else {
        // if there is no other feature, then give feedback
        alert("No matches found");
      }
    }
  }.bind(this));
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

Another approach would be to send all requests at the same time (like you're doing now) and then take a look at all the responses once every requests has returned.  You can do this with Promise.all:

var TheAdr; // string 
var features = [...] // array of features

var promises = mapToPromises(features, TheAdr);

// wait for all requests to be fulfilled, then check the responses
Promise.all(promises).then(function(results){
  var foundResult = false;
  for (var i=0; i<results.length; i++){
    if (results[i].features.length === 1) {

      var objectidOfAdr=fset.features[0].attributes['objectid']; 
      var geoAdr=fset.features[0].geometry; 

      var FLayer1 = map.getLayer("adresse"); 
      FLayer1.setSelectionSymbol(marker);
      var selectionQuery1 = new Query();
      selectionQuery1.where = "objectid ="+objectidOfAdr;
      FLayer1.selectFeatures(selectionQuery1,esri.layers.FeatureLayer.SELECTION_NEW);

      ExtentAdr=new Extent(geoAdr.x, geoAdr.y, (geoAdr.x)+50, (geoAdr.y)+50, geoAdr.spatialReference);
      FLayer1.on("selection-complete", zoomToAdr);
      foundResult = true;
      break;
    }
  }

  if (!foundResult){
    alert("Didn't find any results");
  }
});

// takes the list  of features and the address, returns an 
// array of promises
function mapToPromises(features, TheAdr){
  return features.map(feature, function(){
    // construct the query
    var queryAdr = new Query();
    queryAdr.where = "Text_ = '"+TheAdr+"' AND ID_TRONCON ="+idTroncon;
    queryAdr.outSpatialReference = {wkid:102100};
    queryAdr.returnGeometry = true;
    queryAdr.outFields = ["objectid"];
    // submit the query, retuen the promise;
    return queryTaskAdr.execute(queryAdr);
  });
}

My advice would be to use Ken's approach though.

SaraEL_MALKI
Occasional Contributor II

your first proposition is EXACTLY what I was looking for, I will take your advice in consideration, thank you sir.

0 Kudos