Making Better Promises

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.

This entry was posted in App Developers, Developer, Web and tagged , , , , , . Bookmark the permalink.

Leave a Reply

3 Comments

  1. jtarazona says:

    This is very interesting! Thanks for sharing it!

  2. reiprecht says:

    I think this is a very important change and I’m very glad that instances like the MapView can now used safely with native promises.

    I only wonder why you have decided not to drop the inheritance from esri/Promise at all.

    In my opinion a common “load” Method which returns a Promise would have clear expressed what the purpose of “when” or “then” in the domain classes like MapView is (exactly like in the Map sample).

    Of course your current solution is not breaking to much and can simple be adopted, thanks for that!

    • Yann Cabon says:

      Hello Marko,

      Thanks for you comment.

      I only wonder why you have decided not to drop the inheritance from esri/Promise at all.

      In my opinion a common “load” Method which returns a Promise would have clear expressed what the purpose of “when” or “then” in the domain classes like MapView is (exactly like in the Map sample).

      That was an option we thought about and definitely would have been more explicit. We ended up with when to facilitate the migration, like you mention, and also to not get stuck having to chose an expressive name for different context, while it does the same thing. All in all, every solutions we laid on the table had pros and cons.