0

I have an issue with JavaScript Promise where in one of the promise is not moving into then()

Below is the code I am trying to accomplish.

Background: I have not used Promise before, but have read through few articles.

    function doStart(){        
    var loadedMap = loadBasemap([layer0]);
    loadedMap.then(function (v) {
            view = loadView(MAP_CANVAS_ID, map);
            ...
        });
    ...
}
    
 function loadBasemap(layers) {
    if (LayerSettings && LayerSettings.hasOwnProperty('basemap') && LayerSettings.basemap.hasOwnProperty('baseMapLayers')) {
        new Promise(function () {
            updateToDefaultLayerSetting();
        }).then(function () {
            map = new Map({
                basemap: Basemap.fromJSON(LayerSettings.basemap),
                layers: layers
            });
        });
        return new Promise((resolve, reject) => resolve(map));
    }
    else {...}
}

async function updateToDefaultLayerSetting() {
    console.log("Calling default");
    const result = await actionDefaultBasemap();
    console.log(result);
}

var defaultBasemap;
function actionDefaultBasemap() {
    let portalA = new Portal(portalConfig);
    
    new Promise(function () {
        portalA.load().then(function () {
            defaultBasemap = portalA.useVectorBasemaps ? portalA.defaultVectorBasemap : portalA.defaultBasemap;
        }).then(function () {
            if (LayerSettings.basemap.baseMapLayers[0].hasOwnProperty('url') && typeof defaultBasemap.resourceInfo !== "undefined") {
                LayerSettings.basemap.baseMapLayers[0].id = defaultBasemap.resourceInfo.data.baseMapLayers[0].id;
                LayerSettings.basemap.baseMapLayers[0].title = defaultBasemap.resourceInfo.data.baseMapLayers[0].title;
                LayerSettings.basemap.baseMapLayers[0].url = defaultBasemap.resourceInfo.data.baseMapLayers[0].url;
            }
        });
    })

    return new Promise((resolve, reject) => resolve(defaultBasemap));
}

Execution Order is as below:

  1. doStart()
  2. loadBasemap(layers)
  3. updateToDefaultLayerSetting()
  4. actionDefaultBasemap()

The process is execulting the then() inside actionDefaultBasemap() but not reaching the then() in loadBasemap().

Not sure where the process is breaking. I would need the map object to be returned to

view = loadView(MAP_CANVAS_ID, map);

Any advise would be helpful.

jats
  • 87
  • 1
  • 1
  • 10
  • 3
    I think you need to read more articles. The callback function passed into `new Promise()` has to accept the `resolve` and `reject` parameters, and call them as appropriate. Otherwise the Promise will never resolve (or reject) and nothing will happen. – Pointy Jun 23 '23 at 03:21
  • `new Promise(function () { updateToDefaultLayerSetting(); })` - you're never `resolve`ing the promise! – Bergi Jun 23 '23 at 03:21
  • 4
    Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Jun 23 '23 at 03:21
  • Also note that `map` and `baseMap` would not be defined at the point where you are returning new Promise. All in all, you'd need to do `return updateToDefaultLayerSetting().then(function () { return new Map(...) })` — this creates the promise from the async function call, `then` makes a new promise which awaits the previous one, then that promise gets returned. – Amadan Jun 23 '23 at 03:37
  • Does this answer your question? [Promise.then not executing](https://stackoverflow.com/questions/43896030/promise-then-not-executing) – Heretic Monkey Jul 19 '23 at 18:58

1 Answers1

5
new Promise(function () {
   updateToDefaultLayerSetting();
}).then(...

The new Promise constructor exists for one reason: to wrap a promise around something that is not using promises (eg, a callback). If you're going to use it for that, you need to call either the resolve or reject callbacks, as in the following:

new Promise(function (resolve, reject) {
  setTimeout(() => {
    if (Math.random() > 0.5) {
      resolve('success!');
    } else {
      reject('error');
    }
  }, 1000);
});

Your code never calls resolve or reject, and so the code in the .then can never happen

But you actually don't need to call new Promise at all, because you already have promises! updateToDefaultLayerSetting returns a promise (which you can tell because it's an async function), which means that loadBaseMap can be written as the following:

function loadBasemap(layers) {
  if (LayerSettings && LayerSettings.hasOwnProperty("basemap") && LayerSettings.basemap.hasOwnProperty("baseMapLayers")) {
    return updateToDefaultLayerSetting().then(function () {
      return new Map({
        basemap: Basemap.fromJSON(LayerSettings.basemap),
        layers: layers,
      });
    });
  } else {...}
}

In the above code, calling .then on a promise creates a new promise. That new promise will resolve with whatever is eventually returned by the callback function. So loadBasemap is returning a promise which will resolve to a Map.

If you prefer to do the same with async/await (i know i do), that will look like:

async function loadBasemap(layers) {
  if (LayerSettings && LayerSettings.hasOwnProperty("basemap") && LayerSettings.basemap.hasOwnProperty("baseMapLayers")) {
    await updateToDefaultLayerSetting();
    return new Map({
      basemap: Basemap.fromJSON(LayerSettings.basemap),
      layers: layers,
    });
  } else {...}
}

actionDefaultBasemap will need some rewriting as well, which i have not included. It has the same problem: creating new promises where none are needed, and then never resolving the new promise. portalA.load() already returns a promise, so build off of that.

If you prefer .then syntax, call .then on the promise and in the callback return what you want the new promise to resolve to. If you prefer async/await syntax, await the promise and then return what you want the function's promise to resolve to.

Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98