GEOG 863:
Web Application Development for Geospatial Professionals

6.2.3 Promises


The folks who run the Mozilla Developer Network define a promise object as follows:

The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.

The basic idea behind promises in JavaScript (and this goes well beyond geospatial apps) is that a lot of the operations that scripts perform take some time to complete. Rather than grinding the user experience to a halt while waiting for one of these operations, the browser gets to work on it using part of the device’s resources, but continues on executing the code that comes next. The notion of a promise came about as a way to simplify the coding of applications that contain asynchronous operations.

A promise can be in one of three states: resolved, rejected, or pending. As a developer working with a promise, you can write code to be executed when the promise resolves successfully and when it is rejected (fails to finish successfully).

Returning to the previous example, running a query against a layer on some server somewhere takes some time. So Esri wrote the queryFeatureCount() method to return a promise. As is typical, working with this promise typically involves calling on its then() method. The then() method requires that you specify a callback function to be executed when the promise has been resolved. In the example, I inserted an anonymous function, though it is also acceptable to plug in the name of a function that’s been defined elsewhere. This can be a good idea when the function is relatively long.

Referring back to the definition of a promise object, when a promise is resolved successfully, it returns a value. In the case of queryFeatureCount(), as explained on its page in the SDK, the returned value is the number of features meeting the query criteria. Something that is a bit tricky getting used to in working with promises is that the return value is passed along to the callback function specified in the then() method call. When defining the function, you need to create a variable to hold that passed value. In my example, I called this variable count; in the SDK example, it’s called numFeatures. The important thing is that you know the data type being returned and write your code to work with it properly.

As mentioned, you can also write code to be run in the event that the promise is rejected (fails). This error handling function should come immediately after the success handling function. The example below again uses queryFeatureCount(), this time with a misspelling of the field name in the query. Note the second anonymous function embedded within the then() method, which logs an error to the browser console when queryFeatureCount() fails.

See the Pen Promise Rejected Demo by Jim Detwiler (@jimdetwiler) on CodePen.

Now that you know how to handle methods that return a promise, you should be aware that there are certain classes in the API (MapView, SceneView, and all of the Layer sub-classes) that also return a promise when you create an instance of the class. So when you create a MapView, for example, you can use then() on that object to define what you want to happen when that view is ready. Handling an object as a promise was actually an important part of the queryFeatureCount() example we’ve been discussing. The FeatureLayer referenced by the counties variable takes a moment to load, so the counties.then on line 34 essentially tells the browser to wait to execute the queryFeatureCount() method until that layer has finished loading. Removing that part of the script causes it to break, as you can see in this example.

See the Pen queryFeatureCount() Demo (Broken) by Jim Detwiler (@jimdetwiler) on CodePen.

The Guide section of the SDK provides further reading on Working with promises.