How to properly check if a FeatureLayer object has been loaded?

6875
11
04-27-2018 01:46 PM
SamuelAbati
New Contributor III

So, I know the documented way to do it would be like this :

const fl = new featureLayer(url);
fl.on('load', () => {
   doSomething();
})‍‍‍‍‍‍‍‍

But there is a problem with the approach above, if the featureLayer has already loaded between its creation and the event binding, doSomething() will never trigger.

This would be solved by simply having a callback parameter on the featureLayer constructor, which doesn't.

So, for now I'm using the following solution :

const fl = new featureLayer(url);
if(!fl.loaded){             
    fl.on('load', () => {                    
        doSomething();                
    })
}else{                
    doSomething();
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

But there's also a very nasty possible error with my temporary solution, it can happen that the featureLayer will load between the if/else check, and then doSomething() will never trigger.

Is there an actual solution to this? Is this a bug? Am I missing something?

Here's a pen showing the issue : FeatureLayer 

I'm using the setTimeout to force the layer to load, in our company the arcgis server is local so they load really fast, before binding the event as I stated above

0 Kudos
11 Replies
MunachisoOgbuchiekwe
New Contributor III

Hello Samuel, 

This may or may not be a solution to your problem, but if you were using the 4.x API you can use a promise to solve this. I am assuming you are using 3.x, so you can wrap the load event in a promise and essentially get the same affect. 

require([
    "esri/map",
    "esri/layers/FeatureLayer",
    "dojo/domReady!"
], function (Map,FeatureLayer) {

    //Initializing the map constructor

    var map = new Map("map", {
        center: [-118, 34.5],
        zoom: 8,
        basemap: "topo"
    });
     
     var fl = new FeatureLayer("https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/0");
     
     //create a promise
     var promise = new Promise((resolve, reject) => {
        fl.on('load', (e) => {
            console.log(e);
            resolve(e.layer.loaded)
            reject("Something is wrong");
        })
     });
     
    promise.then((e) => {
        if(e){
            console.log("The layer has loaded");
        }
    });
});‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
SamuelAbati
New Contributor III

Yes I'm using the 3.X

Are you positive this method works even if the featureLayer loaded before creating the promise?

0 Kudos
SamuelAbati
New Contributor III

See this pen : FeatureLayer 

It didn't solve it I'm afraid.

I'm not confident I could do it as the source is minified, but if this is a missing feature, it could be solved by overwriting the FeatureLayer constructor with a callback.

0 Kudos
MunachisoOgbuchiekwe
New Contributor III

Ah, okay I see what you are saying now. Yea, I don't think this workflow would work in 3.x. In 4.x a lot of the modules have promises that allow you to get a result back once the object has loaded. Since 3.x is event driven, you wouldn't be able to get the functionality you want unless, like you said, add in that functionality yourself.

0 Kudos
SamuelAbati
New Contributor III

To me it feels more like a bug than a missing feature.

Unfortunately all our codebase is already built upon 3.x, I can't change that now.

In the example I use the setTimeout to force the layer to load, because in our company the arcgis server is local so they featureLayer object loads really fast, before binding the event as I stated in the question.

Couldn't you help me implement this in 3.x? Not having the non-minified source makes it really difficult to reverse-engineer.

0 Kudos
FreddieGibson
Occasional Contributor III

Hi Samuel, 

Are you able to replicate a situation where the FeatureLayer loads before the next line of code can execute to wire into the load event? If so would you be able to record a video of that and send it to me? I'd be very interested in replicating this if possible. 

0 Kudos
SamuelAbati
New Contributor III

Isn't the CodPen I linked sufficient? I just used setTimeout to wait untill the layer is loaded before binding the event.

As I mentioned, our ArcgisServer is local and sometimes the layer will load before the onLoad event is bound (if it's a small layer, or lets say the user has several tabs open making javascript a bit slow) . Its just a design flaw in my point of view, its pure Murphy's Law. This error can happen, and in my case it did.

Not trying to be rude, but I'm not sure what other proof I can provide, what I can't is post a video of my company's code

0 Kudos
FreddieGibson
Occasional Contributor III

Hi Samuel,

I do know that depending on the browser it is possible for the layer to load as soon as it's constructed, but I rarely run into this situation. My goal is to help you to be able to move forward with the use of the 3.x API. Within the documentation it does mention this scenario against DynamicMapServicesLayers, but I'm curious if you ever implement this in your workflow. Below is the documentation I'm referring to and you'll see that an emit was used to resolve this issue in the event that the layer loads faster than you're able to wire in the event. 

ArcGIS API for JavaScript- Working with Events

https://developers.arcgis.com/javascript/3/jshelp/inside_events.html 

In Internet Explorer, due to resource caching, the onLoad event is fired as soon as the layer is constructed. Consequently you should check whether the layer's loaded property is true before registering a listener for the "load" event:

on example:

require(["esri/layers/ArcGISDynamicMapServiceLayer", ...
], function(ArcGISDynamicMapServiceLayer, ... ) {
  var layer = new ArcGISDynamicMapServiceLayer(...);
  if(layer.loaded){
    printInitialExtent({"layer":layer});
  } else {
    layer.on("load", printInitialExtent);
  }

  function printInitialExtent(evt) {
    console.log(evt.layer.initialExtent);
  }
});

However, on has a companion function, emit. Instead of directly calling the event handler, we could have forced the event to fire using emit:

on example:

require(["esri/layers/ArcGISDynamicMapServiceLayer", ... ], function(ArcGISDynamicMapServiceLayer, ... ) {
  var layer = new ArcGISDynamicMapServiceLayer(...);
  layer.on("load", printInitialExtent);
  if(layer.loaded){
    layer.emit("load",{
      "layer":layer
    });
  }

  function printInitialExtent(evt) {
    console.log(evt.layer.initialExtent);
  }
SamuelAbati
New Contributor III

Hello and thanks for the reply.

Your first example from the documentation is exactly like my second example.

And your second example although I wasn't aware you could do that, still shares the same problem as your first example.

The layer could be loaded between the if/else or the event binding/if.

Yes I understand its very rare, call me perfectionist, but it's just not the correct flow of things. Mixing synchronous code with asynchronous code will never be the ideal solution.

If you can't help me any further that's fine, but I believe the only way is to extend the FeatureLayer class, I just don't know how because I don't have the source.

0 Kudos