Executing a series of queries inside a loop

5594
7
Jump to solution
05-05-2016 12:06 PM
NaciDilekli
Occasional Contributor

Hello,

I am trying to loop through a string array of place names, build a query statement for each place, get the extent for the place, and build a bookmarks list. I am however baffled with how the results are built. Basically, it looks like while the query task is located inside the loop, it waits for the loop to finish to be executed. Therefore I get multiple and identical query results in the end. The core of the problem can be shown with this piece of code:

for (i = 1; i < allSysNames.length; i++) {

     query.where = "SysName =" + allSysNames;

     console.log("i inside the loop is: " + i);

     queryTask.executeForExtent(query, function(result){

          console.log("i inside query task equals to: " + i);

     });

}

console.log("outside the loop");

The log is the following.

i inside the loop is: 1

i inside the loop is: 2

i inside the loop is: 3

outside the loop

i inside query task equals to 4

i inside query task equals to 4

i inside query task equals to 4

I am probably missing something very basic here, as I am new to Javascript and the API. What do I need to execute the queryTask for my purposes? Thank you!

Naci

0 Kudos
1 Solution

Accepted Solutions
ReneRubalcava
Frequent Contributor

Those query tasks are asynchronous, so won't be executed immediately.

You have a couple of options

var queries = [];
for (i = 1; i < allSysNames.length; i++) {
    query.where = "SysName =" + allSysNames;
    console.log("i inside the loop is: " + i);
    queries.push(queryTask.executeForExtent(query));
}
// dojo/promise/all
all(queries).then(, function(results){
    // another for loop
    for (var j = 0; j < results.length; j++) {
        console.log("j inside query task equals to: " + j);
    }
});
console.log("outside the loop");


// or


// I prefer this method
var queries = allSysNames.map(function(name) {
    query.where = "SysName =" + name;
    return queryTask.executeForExtent(query)
});
// dojo/promise/all
all(queries).then(, function(results){
    // another for loop
    for (var j = 0; j < results.length; j++) {
        console.log("j inside query task equals to: " + j);
    }
});
console.log("outside the loop");

I have a couple of posts on Promises you could check out.

Keeping Promises

ArcGIS JavaScript Promises - odoenet

Since the Promises is asynchronous, it will finish after your last console statement.

Hope that helps a bit.

View solution in original post

7 Replies
ReneRubalcava
Frequent Contributor

Those query tasks are asynchronous, so won't be executed immediately.

You have a couple of options

var queries = [];
for (i = 1; i < allSysNames.length; i++) {
    query.where = "SysName =" + allSysNames;
    console.log("i inside the loop is: " + i);
    queries.push(queryTask.executeForExtent(query));
}
// dojo/promise/all
all(queries).then(, function(results){
    // another for loop
    for (var j = 0; j < results.length; j++) {
        console.log("j inside query task equals to: " + j);
    }
});
console.log("outside the loop");


// or


// I prefer this method
var queries = allSysNames.map(function(name) {
    query.where = "SysName =" + name;
    return queryTask.executeForExtent(query)
});
// dojo/promise/all
all(queries).then(, function(results){
    // another for loop
    for (var j = 0; j < results.length; j++) {
        console.log("j inside query task equals to: " + j);
    }
});
console.log("outside the loop");

I have a couple of posts on Promises you could check out.

Keeping Promises

ArcGIS JavaScript Promises - odoenet

Since the Promises is asynchronous, it will finish after your last console statement.

Hope that helps a bit.

KenBuja
MVP Esteemed Contributor

One more useful thing to know about Promises is that they are returned in the same order that they are passed in. Therefore, you'll always know that result1 will be what's returned from query1, result2 will be what's returned from query2, etc

NaciDilekli
Occasional Contributor

Thank you very much! This helps a lot. I have a couple of follow up questions. First, Aptana gives me an error for the use of comma in this line

all(queries).then(, function(results){

I removed the comma, and it seems to be working fine. Is that OK?

Second issue is another one that confuses me. As I mentioned before, I use this loop to build a bookmarks list (following this example Bookmarks widget | ArcGIS API for JavaScript), which I defined as

bookmarks = new esri.dijit.Bookmarks({

    map: map,

}, dojo.byId('bookmarks'));

I then created a temp bookmark:

var tempBookmark ={

          "extent": {

            "spatialReference": {

                "wkid": 0

            },

            "xmin":0,

            "ymin":0,

            "xmax":0,

            "ymax":0

          },

          "name": "Temp"

      };

In the procedure you described, I assigned the extents of the results to the tempBookmark. I also assigned the tempBookmark name using allSysNames. Here is the more complete piece of the code:

all(queries).then(function(results){

    // another for loop

    for (var j = 0; j < results.length; j++) {

          console.log("j inside query task equals to: " + j);

          console.log(results.extent);

          tempBookmark.extent = results.extent;

          tempBookmark.name = allSysNames;

          console.log(tempBookmark.extent);

          bookmarks.addBookmark(tempBookmark);

}

});

So the bookmark list is built just fine, with the correct names from the allSysNames array. Apparently the statement tempBookmark.name = allSysNames; works as expected. However the problem is with the actual extents. I have 5 items in the bookmarks list, and no matter which one I click, it zooms to the extents of the last item in the array. For debugging purposes I logged the values of the elements (xmin, xmax, ymin, ymax) of tempBookmark.extent, and they are same as corresponding results.extent values. But those values apparently do not properly get transferred to the bookmarks. It is strange because the bookmark name appears to be updated just fine.

0 Kudos
ReneRubalcava
Frequent Contributor

For the first one, that was a typo on my part, sorry.

For second one

The tempBookmark you are updating is a single bookmark, so you keep updating the same one and adding it multiple times, but inside the bookmark widget, you basically added the same bookmark multiple times.

Create a new bookmark on each loop and you should be ok.

NaciDilekli
Occasional Contributor

Thanks a lot, that solved my problem!

0 Kudos
JamesOsundwa
New Contributor III

Hi Rene,

Why doesn't this work? Where visible is coming from an array of layerId's that are providing indexes for myQuery (it works for any random array eg ["alpha", "beta", "charlie"]:

var myQuery = ["Population>10000" , "Hispanic > 1500" , "Employees > 10000"];

var result = visible.map(function(e) {
return myQuery;
});

0 Kudos
JohnRitsko1
New Contributor III

Rene, I hope you have chance to see this as I have a similar question as this.  I understand that I can't call a query inside of a for loop.  Here is what I am trying to do.  I reach out to an API that returns information on Bus Stops that are currently closed.  I want to display these Bus Stops on a map but the information from the API doesn't include lat/lon.  So what I do is take the response from the API and iterate through them getting the Bus Stop number and then running a query on our Bus Stop Feature Class that can return the lat/lon that I in turn create a graphics layer from.  Problem of course is the query doesn't work as expected.  Wondering how I can incorporate your answer above into my scenario. 


layerList.on("trigger-action", (event) => {
const id = event.action.id;
if (id === "refresh-ClosedBusStops") {
var settings = {
"url": 'api to get closed bus stops',
"method": "GET",
"timeout": 0,
"headers": {
"Authorization": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Content-Type": "application/json"
}
};
$.ajax(settings).done(function (response) {
    var Xcoord = 0;
    var Ycoord = 0;
    for(i = 0; i < response.adjustments.length; i+=1) {
      busID = response.adjustments[i].details.stopIds;
      routeAffected = response.adjustments[i].details.routesAffected;
      notes = response.adjustments[i].notes;
      reason = response.adjustments[i].reason;
      const query = new Query();
      var queryValue = "Id = " + response.adjustments[i].details.stopIds;
      query.where = queryValue;
      query.outFields = [ "Xcoord", "Ycoord" ];
      query.returnGeometry = true;
      ClosedBusStops.queryFeatures(query).then(function(results) {
          Xcoord = results.features[0].attributes.Xcoord;
          Ycoord = results.features[0].attributes.Ycoord;
          createClosedBusStop(busID, routeAffected, notes, reason, Xcoord, Ycoord);
      });
    };
});
}
});

function createClosedBusStop(busID, routeAffected, notes, reason, Xcoord, Ycoord) {
const busStop = new Point({
  type: "point",
  longitude: Xcoord,
  latitude: Ycoord,
  wkid: 4326
});

var markerSymbol = {
  type: "simple-marker",
  style: "square",
  color: "RED",
  size: "10px"
};

popupClosedBusStops = {
  title: "Bus Stop: " + busID,
  content:"<b>Route(s) Impacted: </b>" + routeAffected + "<br/>" +
  "<b>Notes: </b>" + notes + "<br/>" +
  "<b>Reason: </b>" + reason
};
const busStopGraphic = new Graphic ({
  geometry: busStop,
  symbol: markerSymbol,
  popupTemplate: popupClosedBusStops
});

view.graphics.add(busStopGraphic);
};

0 Kudos