Select to view content in your preferred language

Need help converting a DeferredList to use dojo/promise/all

4130
7
Jump to solution
12-02-2014 12:45 PM
AlanaBaker1
Deactivated User

I need some help before I lose what shred of sanity I have left.  Let me start out by saying that I am pretty new to JavaScript and dojo. I am trying to rewrite our JS maps to AMD and have hit a wall when it comes to converting a DeferredList to the new dojo/promise API.

Not to overwhelm you with code, I'll just paste a snippet of the "old" code (I am aware of converting to array.map etc. but just want to show what I'm starting with).

var tasks = dojo.map(layers, function(layer) {

                    return new esri.tasks.IdentifyTask(layer.url);

                });
//map each visible dynamic layer to a new identify task, using the layer url

                var defTasks = dojo.map(tasks, function (task) {

                    return new dojo.Deferred();

                });
//map each identify task to a new dojo.Deferred

              

                var dlTasks = new dojo.DeferredList(defTasks); //And use all of these Deferreds in a DeferredList

                dlTasks.then(showIDResults); //chain showResults nto your DeferredList ***PROBLEM HERE****

                idParams.width = map.width;

                idParams.height = map.height;

                idParams.geometry = evt.mapPoint;

                idParams.mapExtent = map.extent;

                         alert(tasks.length);

                for (i=0;i<tasks.length;i++) { //Use 'for' instead of 'for...in' so you can sync tasks with defTasks

                    try {

                        tasks[i].execute(idParams, defTasks[i].callback, defTasks[i].errback); //Execute each task

                    }
                    catch (e) {

                        console.log("Error caught");

                        console.log(e);

                        defTasks[i].errback(e); //If you get an error for any task, execute the errback

                    }

                }

The showIDResults function expects an array of results from execution of the tasks.  This is were I'm having a problem. No matter what I try (and I've tried a lot of things) I end up with a Deferred or a Promise rather than actual results to pass to showIDResults.

Am I making any sense? Any help would be greatly appreciated!

0 Kudos
1 Solution

Accepted Solutions
KenBuja
MVP Esteemed Contributor

In this code, I'm creating IdentifyTasks for all the visible layers (which come from different services) on my map.

    function mapClickHandler(evt) {
        map.graphics.clear();
        layerResultsGraphic.clear();

        var idPoint = evt.mapPoint;
        var layers = array.map(map.layerIds, function (layerId) {
            return map.getLayer(layerId);
        });
        layers = array.filter(layers, function (layer) {
            if (layer.visibleLayers !== undefined) {
                if (layer.visibleLayers[0] !== -1) { return layer.getImageUrl && layer.visible; }
            }
        });

        var identifyParamsList = [];
        array.forEach(layers, function (layer) {
            var idParams = new IdentifyParameters();
            idParams.width = map.width;
            idParams.height = map.height;
            idParams.geometry = evt.mapPoint;
            idParams.mapExtent = map.extent;
            idParams.layerOption = IdentifyParameters.LAYER_OPTION_VISIBLE;
            idParams.layerIds = layer.visibleLayers;
            idParams.tolerance = 3;
            idParams.returnGeometry = true;
            idParams.spatialReference = map.spatialReference;
            identifyParamsList.push(idParams);
        });

        var tasks = array.map(layers, function (layer) {
            return new IdentifyTask(layer.url);
        });
        var defTasks = array.map(tasks, function (task) {
            return new Deferred();
        });
        var promises = [];

        for (var i = 0; i < tasks.length; i++) {
            promises.push(tasks.execute(identifyParamsList));
        }

        var allPromises = new all(promises);
        allPromises.then(function (r) {
            showIdentifyResults(r, tasks, idPoint);
        });

    }

View solution in original post

0 Kudos
7 Replies
MatthewLofgren
Frequent Contributor

Here's an example from some code I have using dojo/promise/all (dojo/promise/all replacing deferredlist). I trimmed out unnecessary code.

Note that the deferreds have a 'promise' property you pass to 'all'.

//Execute 3 query tasks. store into 3 separate deferreds
var pointDeferred = pointQueryTask.execute(query);
var segmentDeferred = segmentQueryTask.execute(query);
var textDeferred = textQueryTask.execute(query);


// pass the an array of promises to all
all([pointDeferred.promise, segmentDeferred.promise, textDeferred.promise]).then(function(results){
  // results are returned as an array with the same order as the promises were passed
  var points = results[0].features;
  var segments = results[1].features;
  var text = results[2].features;

  console.log(results);

  },
  function(err){
  console.log("error");
});
0 Kudos
KenBuja
MVP Esteemed Contributor

In this code, I'm creating IdentifyTasks for all the visible layers (which come from different services) on my map.

    function mapClickHandler(evt) {
        map.graphics.clear();
        layerResultsGraphic.clear();

        var idPoint = evt.mapPoint;
        var layers = array.map(map.layerIds, function (layerId) {
            return map.getLayer(layerId);
        });
        layers = array.filter(layers, function (layer) {
            if (layer.visibleLayers !== undefined) {
                if (layer.visibleLayers[0] !== -1) { return layer.getImageUrl && layer.visible; }
            }
        });

        var identifyParamsList = [];
        array.forEach(layers, function (layer) {
            var idParams = new IdentifyParameters();
            idParams.width = map.width;
            idParams.height = map.height;
            idParams.geometry = evt.mapPoint;
            idParams.mapExtent = map.extent;
            idParams.layerOption = IdentifyParameters.LAYER_OPTION_VISIBLE;
            idParams.layerIds = layer.visibleLayers;
            idParams.tolerance = 3;
            idParams.returnGeometry = true;
            idParams.spatialReference = map.spatialReference;
            identifyParamsList.push(idParams);
        });

        var tasks = array.map(layers, function (layer) {
            return new IdentifyTask(layer.url);
        });
        var defTasks = array.map(tasks, function (task) {
            return new Deferred();
        });
        var promises = [];

        for (var i = 0; i < tasks.length; i++) {
            promises.push(tasks.execute(identifyParamsList));
        }

        var allPromises = new all(promises);
        allPromises.then(function (r) {
            showIdentifyResults(r, tasks, idPoint);
        });

    }
0 Kudos
AlanaBaker1
Deactivated User

It works! It works! IT WORKS!!!!! Ken, I can't thank you enough!!!!  Matthew, thank you as well; your information will be valuable to me in other aspects but Ken is doing exactly what I am trying to do.

I have struggled with this for days!  May I ask you both, what is your source of knowledge on this?

0 Kudos
KenBuja
MVP Esteemed Contributor

I agree with John Grayson‌ that this can be a slow operation, particularly if many layers are visible. However, this is what was required by our clients to examine all the features found at a point. Plus, I wanted to find out which service each result came from.

I had the help of Esri support to figure this out.

JohnGrayson
Esri Regular Contributor

Alana,

    performing an Identify operation on many layers can be a costly operation and not something we'd generally recommend, you might want to take a step back and figure out if there's a better way to do what you need.  Regardless, here's a different way to aggregate many Identify calls using AMD style coding, this is NOT the exact code, just a guideline.  Also, you might have to modify 'showIDResults' as the response structure might be different.

// What about sublayer ids?  Each layer might have different sublayer ids...
idParams.width = map.width;
idParams.height = map.height;
idParams.geometry = evt.mapPoint;
idParams.mapExtent = map.extent;

// arrayUtils = require("dojo/_base/array")
var executeDeferreds = arrayUtils.map(layers, function(layer) {
  // assumes each layer supports IdentifyTask
  var identifyTask = new IdentifyTask(layer.url);    
  return identifyTask.execute(idParams);
});

// all = require("dojo/promise/all")
all(executeDeferreds).then(showIDResults);
0 Kudos
AlanaBaker1
Deactivated User

Thank you, John. I can see where this would be a costly operation but it is something we require. Fortunately, we are doing it on a limited number of layers.

I've tried code very similar to yours to no avail but I did get Ken's working. I may take a second look at yours as it is so similar to what I was doing and I hate being defeated.

0 Kudos
OwenEarley
Frequent Contributor

EDIT:

?? - GeoNet was showing this with no answers until after I posted this. Not sure why but hope this helps anyway.

Check the source code in this sample.

Basically the sample takes a map click event and then creates 4 identify tasks 200m in each direction from the map click point.

        ...  
          // Create multiple identify target points
          pts = [];
          pts.push(event.mapPoint.offset(0, 200));
          pts.push(event.mapPoint.offset(200, 0));
          pts.push(event.mapPoint.offset(0, -200));
          pts.push(event.mapPoint.offset(-200, 0));
          console.log(pts);
          
          // Create multiple identify tasks          
          var tasks = [];
          for (var i=0;i<pts.length;i++){
            tasks.push(singleIdentifyTask(pts))
          }
        ...  
        
        // this returns a single task (deferred)
        function singleIdentifyTask(pt){
          console.log("Creating Identify Task: ", pt);
          identifyParams.geometry = pt;
          return identifyTask.execute(identifyParams);          
        }

The code then runs the tasks within dojo/promise/all and processes the results when all tasks are completed:

          // Run tasks
          console.log("Running Tasks");
          all(tasks).then(function(taskResults){
            
            map.graphics.clear();
            for (var i=0;i<pts.length;i++){
              map.graphics.add(new Graphic(pts, sms));
            }            
            // results will be an Array
            console.log("Results", taskResults);
            // example: add each result to map
            for (var i=0;i<taskResults.length;i++){
              var taskResult = taskResults;
              for (var j=0;j<taskResult.length;j++){              
                console.log(taskResult);    
                map.graphics.add(new Graphic(taskResult.feature.geometry, sfs));
              }
            } 
            // show identify points on map
            for (var i=0;i<pts.length;i++){
              map.graphics.add(new Graphic(pts, sms));
            } 
            
          });

I have placed console.log statements so you can open a developer console and view the objects as they are being created or used.

Hope this helps.

Owen - SpatialXP