Mapping

Making Better Promises

Updated April 2018: For the 4.7 release of the ArcGIS API for JavaScript, which improves our compatibility with ES Promises.

At the 4.7 release, we made the API compatible with ES promises by default. You are still able to opt-out by setting the has flag to zero:

<script>
  var dojoConfig = {
    has: {
      "esri-promise-compatibility": 0
    }
  };
</script>

 <link rel="stylesheet" href="https://js.arcgis.com/4.7/esri/css/main.css">
 <script src="https://js.arcgis.com/4.7/"></script>

At version 4.8, we will remove the ability to opt-out of ES promise compatibility.

At 4.7, we removed then() as a method in our API, and similarly deprecated always() and otherwise() as API methods (both of which will be removed from the API at the next release). This functionality can still be achieved using when().

These methods only apply to API methods. Native JavaScript implementations will continue to function.

Original Post: December 2017

This blog post will discuss a change to our implementation of promises, a change towards making a better promise. Numerous classes in the ArcGIS API for JavaScript resolve to promises. This is the case for WebMapWebSceneMapViewSceneViewBasemapGroundLayers, and LayerViews.

Typically, you would use the then() method to be advised when one of these objects is ready to be used. In fact, many samples in the ArcGIS API for JavaScript SDK use then() on a MapView or on a SceneView to wait for the view to be ready before executing the rest of the application code.

view.then(function(){
  // do something with loaded view properties here
});

Unfortunately, the then() method on the above classes is not compatible when used with native JavaScript Promises. The web browser will go into an infinite loop, which will make it unresponsive. The example below reproduces the issue. The function returns a Promise, which is supposed to resolve with a newly created WebMap.

function createWebMap(itemId) {
  var webmap = new WebMap({
    portalItem: {
      id: itemId
    }
  });

  // The browser go into an infinite loop.
  return Promise.resolve(webmap);
}

To understand why this is so important, it’s necessary to know how native JavaScript Promise chaining works. When a promise resolves with a result object that exposes a then() method, the Promise calls it to chain automatically. In our ArcGIS API for JavaScript, WebMap and other similar classes resolves with themselves.

webmap.then(function(webmap){
  // webmap is resolved with itself, which also a promise
});

Back when 4.0 development started, this wasn’t an issue. Since then, the JavaScript Promises have been standardized and more developers have confronted this issue. So to improve the compatibility with JavaScript Promises, we decided to roll out a breaking change by renaming then() on all applicable classes to when().

view.when(function(){
  // do something with loaded view properties here
});

The then() method will still work at 4.6, but will throw warnings in the browser console. To prevent these warning messages from appearing, please insert the below has flag into your code. Because this is an important breaking change, we are not removing then() at 4.6, and will instead remove then() at a later version.

Note: this change only affects the classes that implement a then() method. Other asynchronous methods and function are still fully using promises.

While when() should be used at 4.6 on all applicable classes, you must specify the following has flag prior to loading the ArcGIS API for JavaScript for your application to be compatible with native JavaScript Promises. This must be loaded prior to loading the API.

<script>
  var dojoConfig = {
    has: {
      "esri-promise-compatibility": 1
    }
  };
</script>

 <link rel="stylesheet" href="https://js.arcgis.com/4.6/esri/css/main.css">
 <script src="https://js.arcgis.com/4.6/"></script>

Additionally, this change will allow you to take advantage of async/await alongside the ArcGIS API for JavaScript to write asynchronous code that looks synchronous:

var map = new WebMap({
  portalItem: {
    id: "someid"
  }
});

await map.load();
// the code waits for map to load

var view = new MapView({
  container: "viewDiv",
  map: map
});

await view.when();
// do something with the view

In conclusion, promises are an important part of the ArcGIS API for JavaScript. We feel that it is worth the effort to update how we handle promises in order to be more collaborative with the JavaScript and Web GIS communities. Please see the 4.6 Release Notes for even more exciting changes to the ArcGIS API for JavaScript.

About the authors

Passionate about JavaScript, maps, and writing (not necessarily in that order). Big fan of squirrels. Journeyman mapper of Utility Lines and Public Restrooms. Product Engineer on the ArcGIS API for JavaScript team. In Noah’s spare time, he also enjoys parenting.

Connect:

Lead Developer on the ArcGIS API for JavaScript 4.x

Connect:

Next Article

Clustering and Binning Features in Map Viewer

Read this article