How do I access the results of querytask.execute. Why do they show as undefined yet I can see them in the object?

225
8
Jump to solution
09-03-2019 09:38 AM
JamesHone1
New Contributor III

HI I am using queryTask to query a feature layer, if I log the resulting object I can see my features in there.

But when I attempt to access the features in the object I get undefined yet I can access other parts of the object just fine, I just cant get to the results.

		_onDrawComplete: function(graphic) {
			var geometry = graphic.geometry;
			this.drawBox.deactivate();

			query = new Query();
			query.geometry = geometry;
			query.spatialRelationship = 'esriSpatialRelIntersects';
			query.returnGeometry = true;

			queryTask = new Querytask(this.featureURLs[0]);

			var qresults = queryTask.execute(query);

			console.log(qresults);
			console.log(qresults.fired);
			console.log(qresults.results);‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

As you can see from my log below, when I log qresults it has contains results with values in it, it even has my features stored in the features array in results[0]['features'].

If I try to log qresults.fired I get the value, but if I try to log qresults.results I get undefined even though I can see that it is defined in the previous log of qresults.

Can someone explain why I am seeing results as undefined directly after seeing it with values in it?

Cheers

Jim

0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Esteemed Contributor

James,

  You have not defined a callback function for the results of the execute.

queryTask.execute(query, lang.hitch(this, function(results){
  //now you have access to the query results.
}));‍‍‍

The reason is a queryTask returns a deferred and the execute is an asynchronous process, so you have to wait for the deferred to resolve before you have access to the results. In my above code it will not get to the function portion until the deferred is resolved.

View solution in original post

8 Replies
RobertScheitlin__GISP
MVP Esteemed Contributor

James,

  You have not defined a callback function for the results of the execute.

queryTask.execute(query, lang.hitch(this, function(results){
  //now you have access to the query results.
}));‍‍‍

The reason is a queryTask returns a deferred and the execute is an asynchronous process, so you have to wait for the deferred to resolve before you have access to the results. In my above code it will not get to the function portion until the deferred is resolved.

JamesHone1
New Contributor III

thanks again robert, that did work for me but I am finding callbacks rather confusing.

I'm trying to use this while iterating through a list, the problem I am now seeing is the callback is firing at the end when the value in the list has already changed so my function always returns the name of the last item in the list I am iterating through for each feature.

			for (layer of this.featureURLs){
				console.log('outside function ' + layer['name'] + ' ' + layer['url'])
				queryTask = new Querytask(layer['url']);
				queryTask.execute(query, lang.hitch(this, function(results){
					console.log('inside function ' + layer['name'] + ' ' + layer['url'])
					resultitem = { 
						'name': layer['name'],
						'number': results.features.length,
						'features': results.features
					}
					resultslist.push(resultitem)
				}));
			}

so when this runs it logs all the 'outside function' logs first then logs the 'inside function' logs so when I get to the function portion the list is already iterated through and I get the same result for 'name': layer['name] for all 3 query results.

So I kind of understand whats happening but I dont know how to resolve it.

Do I need to pass the layer['name'] into the callback function somehow when its called? 

I'm still unsure how results gets defined in the above code.

I tried to pass in the layer dict and have my function seperate but then results isn't defined.

			for (layer of this.featureURLs){
				console.log('outside function ' + layer['name'] + ' ' + layer['url'])
				queryTask = new Querytask(layer['url']);
				queryTask.execute(query, lang.hitch(this, this._queryCallback(results, layer)));
			}

		_queryCallback: function(results, layer){
			console.log('inside function ' + layer['name'] + ' ' + layer['url'])
			resultitem = { 
				'name': layer['name'],
				'number': results.features.length,
				'features': results.features
			}
			console.log(resultitem)
		}

.

0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

James,

  This is how you pass extra variables to a callback function.

queryTask.execute(query, lang.hitch(this, this._queryCallback, layer));‍

_queryCallback: function(layer, results){
  ...‍‍‍‍
0 Kudos
JamesHone1
New Contributor III

Thanks again.

I'm still a bit confused but it is working.

In the original one I can see results being passed into the function.

queryTask.execute(query, lang.hitch(this, function(results){...}

In the next one we are just passing the function itself and also throwing layer into the mix with no mention of results.

So I am assuming the output of the thing you are passing the function to is what provides the final argument results and this is always the last argument of your callback? I kind of expected it to be function(results, layer) instead of the other way around. 

queryTask.execute(query, lang.hitch(this, this._queryCallback, layer));

_queryCallback: function(layer, results){
0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

James,

   Yes the callback function will always have the results of the deferred and it is not necessary to specify this in your call to this function. Your thought of the results being first is a common one when developers first begin, but as you see the callbacks return variable will always be the last item in the callback function.

JamesHone1
New Contributor III

Not sure if I should start a new thread for this one, Im really struggling with callbacks a bit.

especially when a loop is involved!

            for (layer of this.featureURLs){
                queryTask = new Querytask(layer['url']);
                queryTask.execute(query, lang.hitch(this, this._queryCallback, layer));
            }
            this.layerselection(this.resultslist)
        },
        _queryCallback: function(layer, results){
            if (results.features.length > 0){
                resultitem = {
                    'name': layer['name'],
                    'number': results.features.length,
                    'features': results.features
                }
                this.resultslist.push(resultitem)
            }
        },

The callback passed into- query pushes the results to my list.

Once that list is populate I want to pass it to layerselection, currently it is going into layerselection before the loop of queryTask's is done.

So it seems I need a nested callback (probably on my way to callback hell)  or I need to start using promises which I think is the .then(function) stuff I have seem.

I looked at a nested callback but as I am looping and calling queryTask a number of times I am confused how to set it up so layerselection only runs after the for loop and all the queryTasks are done.

Going to try and find a good JS callback tutorial somehwere!

0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

James,

   There are a few ways to deal with this. The one that is probably less learning for you would be to have a counter.

            this.lyrCntr = 0;
            for (layer of this.featureURLs){
                queryTask = new Querytask(layer['url']);
                queryTask.execute(query, lang.hitch(this, this._queryCallback, layer));
            }
            this.layerselection(this.resultslist)
        },
 
       _queryCallback: function(layer, results){
            if (results.features.length > 0){
                resultitem = {
                    'name': layer['name'],
                    'number': results.features.length,
                    'features': results.features
                }
                this.resultslist.push(resultitem)
            }
            this.lyrCntr++
            if (this.lyrCntr === this.featureURLs.length){
              this.layerselection(this.resultslist);
            }
        },‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Another way would be be to dojo promise All this will allow you to call a function only when all the queryTask deferreds have been resolved. If you want to use this route then you should start a new thread.

JamesHone1
New Contributor III

Thank you, I understand the count method easily enough, I will implement that so I can continue and take a look at promise all stuff a bit later.

Thanks for all the help!

0 Kudos