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 😎 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
Solved! Go to Solution.
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.
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;
}
});
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.
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 ?
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.
your first proposition is EXACTLY what I was looking for, I will take your advice in consideration, thank you sir.