Is it possible to destroy a QueryTask while it's executing?

2848
9
08-02-2011 07:28 PM
StephenLead
Regular Contributor III
I've built a slider bar control which runs a QueryTask automatically on update - as soon as you release the slider's handle, the query executes.

This works fine if the user waits for the QueryTask to finish before moving the slider again, but if they move the slider again quickly, there may be concurrent QueryTasks in operation.

How can I destroy the first QueryTask when the second QueryTask starts?

The QueryTask is called using an onChange event handler on the slider bar, which runs the line:

searchQueryTask.execute(searchQuery, showSearchResults, errResults);


(I've experimented with disabling the slider bar while the query is running, but this isn't ideal from a usability perspective)

Thanks,
Steve
0 Kudos
9 Replies
derekswingley1
Frequent Contributor
I don't think we have a good way to handle this.

As a general rule, I don't use sliders for things that require a server round trip. They're good for doing things like fading between two map services (changing opacity as the slider moves) or filtering features that are already client side (from a feature layer).

Curious to hear if anyone else has thoughts on this...
0 Kudos
StephenLead
Regular Contributor III
Hi Derek,

Background to this question - I'm trying to keep the UI really simple,  so the user doesn't have to worry about typing in a query. A 2-handled  dojo slider bar works really well for selecting features between a  range, aside from the asynchronous query issue.

Filtering on a feature layer may be an option here. I could also use a Submit button which runs the query, rather than running it onChange.

Any other suggestions would be very welcome.

Cheers,
Steve
0 Kudos
HemingZhu
Occasional Contributor III
Hi Derek,

Background to this question - I'm trying to keep the UI really simple,  so the user doesn't have to worry about typing in a query. A 2-handled  dojo slider bar works really well for selecting features between a  range, aside from the asynchronous query issue.

Filtering on a feature layer may be an option here. I could also use a Submit button which runs the query, rather than running it onChange.

Any other suggestions would be very welcome.

Cheers,
Steve


The main issue here is that there is no events for on going queryTask. The only event that handling querytask process would be onComplete. You can try stop that event to see if you will get expected results. You can achieve this by a couple of steps

1. Using a variable to trace the progress of the query task.
var inProgress =false;
....
var queryEvent =dojo.connect(querytask, "onComplete", querTaskOnComplete);
function sliderOnchange(newValue)
{
    // stop passed event if passed event is still in progress
    if (inProgress ==true)  dojo.stopEvent(queryEvent);
    // start a new query process
    inProgress =true;
    ... do your query here...
}
function querTaskOnComplete(featueSet)
{
   ....do you post query action here....
   //reset inProgress at the end of the function
   inProgress =false;
}

0 Kudos
StephenLead
Regular Contributor III
Hi Heming,

This looks like a good approach - I'll let you know how it goes.

Thanks,
Steve
0 Kudos
StephenLead
Regular Contributor III
Heming,

I'm not having any luck with this. Below is the code I'm trying (a modification of the Query No Map sample with the relevant changes highlighted):

<!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" />
    <!--The viewport meta tag is used to improve the presentation and behavior of the samples 
      on iOS devices-->
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
    <title>Query State Info without Map</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" language="Javascript">
      dojo.require("esri.tasks.query");
      dojo.require("esri.map");

      var queryTask, query;
      var queryEvent;

      function init() {
        //build query
        queryTask = new esri.tasks.QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5");
        //dojo.connect(queryTask, "onComplete", showResults);

        //build query filter
        query = new esri.tasks.Query();
        query.returnGeometry = true;
        query.outFields = ["SQMI","STATE_NAME","STATE_FIPS","SUB_REGION","STATE_ABBR","POP2000","POP2007","POP00_SQMI","POP07_SQMI","HOUSEHOLDS","MALES","FEMALES","WHITE","BLACK","AMERI_ES","ASIAN","OTHER","HISPANIC","AGE_UNDER5","AGE_5_17","AGE_18_21","AGE_22_29","AGE_30_39","AGE_40_49","AGE_50_64","AGE_65_UP"];
      }

      function execute(population) {
        query.where = "POP2007 >" + population;
        //execute query
        try {
            dojo.stopEvent(queryEvent);
        }catch(e){
            console.log("error");
        }
        queryEvent = queryTask.execute(query,showResults);
      }

      function showResults(results) {
        var s = "";
        for (var i=0, il=results.features.length; i<il; i++) {
          var featureAttributes = results.features.attributes;
          for (att in featureAttributes) {
            s = s + "<b>" + att + ":</b>  " + featureAttributes[att] + "<br />";
          }
        }
        dojo.byId("info").innerHTML = s;
      }

      dojo.addOnLoad(init);
    </script>
  </head>
  <body>
    Population is greater than: <input type="text" id="population" value="50000" />
    <input type="button" value="Get Details" onclick="execute(dojo.byId('population').value);" />
    <br />
    <br />
    <div id="info" style="padding:5px; margin:5px; background-color:#eee;">
    </div>
  </body>
</html>


Rather than monitoring the query's state in a variable I figured a try/catch statement should do the same thing, right?

The problem is that the dojo.stopEvent declaration doesn't seem to have any effect. The NET tab in Firebug shows that the query takes around 2 seconds to execute. Pressing the button a few times quickly shows that the previous processes are not cancelled.

Any clues?

Cheers,
Steve
0 Kudos
HemingZhu
Occasional Contributor III
Heming, 

I'm not having any luck with this. Below is the code I'm trying (a modification of the   Query No Map sample with the relevant changes highlighted): 

<!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" />
    <!--The viewport meta tag is used to improve the presentation and behavior of the samples 
      on iOS devices-->
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
    <title>Query State Info without Map</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" language="Javascript">
      dojo.require("esri.tasks.query");
      dojo.require("esri.map");

      var queryTask, query;
      var queryEvent;

      function init() {
        //build query
        queryTask = new esri.tasks.QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5");
        //dojo.connect(queryTask, "onComplete", showResults);

        //build query filter
        query = new esri.tasks.Query();
        query.returnGeometry = true;
        query.outFields = ["SQMI","STATE_NAME","STATE_FIPS","SUB_REGION","STATE_ABBR","POP2000","POP2007","POP00_SQMI","POP07_SQMI","HOUSEHOLDS","MALES","FEMALES","WHITE","BLACK","AMERI_ES","ASIAN","OTHER","HISPANIC","AGE_UNDER5","AGE_5_17","AGE_18_21","AGE_22_29","AGE_30_39","AGE_40_49","AGE_50_64","AGE_65_UP"];
      }

      function execute(population) {
        query.where = "POP2007 >" + population;
        //execute query
        try {
            dojo.stopEvent(queryEvent);
        }catch(e){
            console.log("error");
        }
        queryEvent = queryTask.execute(query,showResults);
      }

      function showResults(results) {
        var s = "";
        for (var i=0, il=results.features.length; i<il; i++) {
          var featureAttributes = results.features.attributes;
          for (att in featureAttributes) {
            s = s + "<b>" + att + ":</b>  " + featureAttributes[att] + "<br />";
          }
        }
        dojo.byId("info").innerHTML = s;
      }

      dojo.addOnLoad(init);
    </script>
  </head>
  <body>
    Population is greater than: <input type="text" id="population" value="50000" />
    <input type="button" value="Get Details" onclick="execute(dojo.byId('population').value);" />
    <br />
    <br />
    <div id="info" style="padding:5px; margin:5px; background-color:#eee;">
    </div>
  </body>
</html>


Rather than monitoring the query's state in a variable I figured a try/catch statement should do the same thing, right? 

The problem is that the dojo.stopEvent declaration doesn't seem to have any effect. The NET tab in Firebug shows that the query takes around 2 seconds to execute. Pressing the button a few times quickly shows that the previous processes are not cancelled. 

Any clues? 

Cheers, 
Steve


When you code runs the first time, the queryEvent is null or undefined. So it will almost be certain that you will catch an exception -dojo.stopEvent(undefined);. You might want to define the queryEvent in your init. The purpose of using a variable to monitor is to avoid uncessary broken flow(catch). After a second thought, I think stopping a query complete event does not really service your purpose. It basically tries to stop an already completed query process not an query that is under process. That is probably why it does not working. Sorry for misleading.
0 Kudos
StephenLead
Regular Contributor III
When you code runs the first time, the queryEvent is null or undefined. So it will almost be certain that you will catch an exception -dojo.stopEvent(undefined);


I'd expect it to throw an error the first time, but not subsequent times. And since it's inside a try/catch, this is a handled error so it should have no effect other than writing something to the console (right?).

I think stopping a query complete event does not really service your purpose. It basically tries to stop an already completed query process not an query that is under process. That is probably why it does not working.


Yeah I think you might be right. Thanks anyway!

Cheers,
Steve
0 Kudos
JohnGrayson
Esri Regular Contributor
Steve, one simple way to handle this issue is by disabling the slider until the results come back.  Another way is to use the dojo.Deferred returned by the QueryTask.execute(...) method.  You could check the 'fired' property and only issue another call if it's not already running.  Or you could try calling the 'cancel()' method before making another call.  The code below is intended to show the general concepts only.

<!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" />
    <!--The viewport meta tag is used to improve the presentation and behavior of the samples
    on iOS devices-->
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
    <title>Query State Info without Map</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" language="Javascript">
      dojo.require("esri.tasks.query");
      dojo.require("esri.map");
      var queryTask, query;
      var queryDeferred;
      function init() {
        //build query
        queryTask = new esri.tasks.QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/3");
        //build query filter
        query = new esri.tasks.Query();
        query.returnGeometry = false;
        query.outFields = ["*"];
      }

      function execute(stateName) {
        // query already running query?
        if(queryDeferred && (queryDeferred.fired > 0)) {
          dojo.byId("info").innerHTML = "Query task is already running...";
        } else {
          dojo.byId("info").innerHTML = "Running query task: " + (new Date()).toLocaleTimeString();
          //execute query
          query.text = stateName;
          queryDeferred = queryTask.execute(query);
          queryDeferred.then(showResults,showError);
        }
      }

      function execute2(stateName) {
        // cancel previous query
        cancelExecute();
        dojo.byId("info").innerHTML = "Running query task: " + (new Date()).toLocaleTimeString();
        //execute query
        query.text = stateName;
        queryDeferred = queryTask.execute(query);
        queryDeferred.then(showResults,showError);
      }

      function showResults(results) {
        dojo.byId("info").innerHTML = "Preparing results...";
        var s = "";
        for (var i=0, il=results.features.length; i<il; i++) {
          var featureAttributes = results.features.attributes;
          for (att in featureAttributes) {
            s = s + "<b>" + att + ":</b>  " + featureAttributes[att] + "<br />";
          }
        }
        dojo.byId("info").innerHTML = s;
      }

      function showError(error) {
        dojo.byId("info").innerHTML += dojo.toJson(error);
      }

      function cancelExecute() {
        if(queryDeferred && (queryDeferred.fired > 0)) {
          queryDeferred.cancel();
          dojo.byId("info").innerHTML = "Query task cancelled.";
        } else {
          dojo.byId("info").innerHTML = "Query task not running...";
        }
      }

      dojo.addOnLoad(init);
    </script>
  </head>
  <body>
    US state name :
    <input type="text" id="stateName" value="C" />
    <input type="button" value="Get Details (not if alreay running)" onclick="execute(dojo.byId('stateName').value);" />
    <input type="button" value="Get Details (cancel first)" onclick="execute2(dojo.byId('stateName').value);" />

    <input type="button" value="Cancel" onclick="cancelExecute();" />
    <br />
    <br />
    <div id="info" style="padding:5px; margin:5px; background-color:#eee;">
    </div>
  </body>
</html>
0 Kudos
StephenLead
Regular Contributor III
you could try calling the 'cancel()' method before making another call.  The code below is intended to show the general concepts only.


Hi John,

Thanks very much for posting this - it looks promising, but I'm finding that the Cancel method doesn't seem to have any effect.

Using execute2 (with the Cancel First button), try searching for A then immediately Z. I'm finding that the results for Z are returned first, but then a few seconds later the results for A overwrite the Z results.

Or try searching for A then immediately hitting Cancel - the results are still returned. This implies that the Cancel hasn't actually worked in either case.

It looks like the best option will be to disable the interface until the query has responded, before allowing a subsequent query.

Cheers,
Steve
0 Kudos