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.
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. This example 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.
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.
The Guide section of the SDK provides further reading on Working with promises.