GEOG 863:
Web Application Development for Geospatial Professionals

7.2.3 Promises

PrintPrint

7.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 example from the previous page, 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 API Reference, 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 API Reference example, it’s called numFeatures. The important thing is that you know the data type being returned -- the API Reference conveys this to you in this case with the return type being listed as Promise<Number> -- 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.

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

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 write code that defines what you want to happen when that view is ready.  It might help to conceptualize this second type of promise as class-based, as opposed to the method-based promises discussed above.

An important change in working with class-based promises occurred with the release of version 4.7 of the API.  Prior to that release, developers would use then() to specify what to do with the object once it is ready to be used.  In other words, then() was used with both types of promises.  Beginning with version 4.7, class-based promises are instead handled using when().  The reasoning behind this change, having to do with compatibility with native JavaScript promises, is detailed in this Esri blog post.  

Returning to the queryFeatureCount() example, handling an object as a promise was actually an important part of the coding.  The FeatureLayer referenced by the counties variable takes a moment to load, so the counties.when on line 34 essentially tells the browser to wait to execute the queryFeatureCount() method until that layer has finished loading.

To help illustrate the change in class-based promise handling, below is the same app written for version 4.6, in which then() is used instead of when().  A lesson to learn here is that the version of the API employed by an app matters.  

See the Pen Untitled by Jim Detwiler (@jimdetwiler) on CodePen.