Rice
Rice

Reputation: 3531

Asynchronous Javascript functions not called correctly in promises

I have the following code:

//this 'hitches' the scope to the appropriate callback method
var hitchWidgetToPopulateHierarchyDefinitionFields = DojoBaseLang.hitch(this, populateHierarchyDefinitionFieldsFromSelectedHierarchyTable);
hitchWidgetToPopulateHierarchyDefinitionFields();

function selectValuesByFieldFromHierarchyTable(currentlySelectedColumn) {
     //query database and return an array of strings
 }

function addHierarchyLevelSelectionToDOM (hierarchyLevelsArray) {
   var temporaryDataStore= [];
   for (var i=0; i<hierarchyLevelsArray.length;i++){
       //DO STUFF
   }
}
function populateHierarchyDefinitionFieldsFromSelectedHierarchyTable(){
    var selectedHierarchyDefinitionColumn = "COLUMN_NAME"
    var p1 = new Promise(function( resolve, reject) {
        setTimeout(function() {
            resolve(selectValuesByFieldFromHierarchyTable(selectedHierarchyDefinitionColumn))
        },2000);
    });
    p1.then(
        function resolve(value) {
            console.log(value);
            addHierarchyLevelSelectionToDOM(value);
        }
    ).catch(
        function reject(error) {
            console.error(error);
        }
    );
}

This results in the console output logging the value but the value is still undefined inside of the addHierarchyLevelSelectionToDOM:

TypeError: Cannot read property 'length' of undefined
Object {Relevant data }

Notice that the object is indeed logged, and the error is caught inside of the catch.

My intention is simply to call addHierarchyLevelSelectionToDOM from the value returned by selectValuesByFieldFromHierarchyTable. The problem is that the value is undefined when addHierarchyLevelSelectionToDOM(value) is called, but the console.log(value) call prints the correct returned value. I then tried multiple promises to the same avail:

var p1 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve(selectValuesByFieldFromHierarchyTable(selectedHierarchyDefinitionColumn))
  }, 2000);
});
var p2 = p1.then(function(value) {
  console.log(value);
  return new Promise(addHierarchyLevelSelectionToDOM(value));
});
p2.then(function(value) {
  console.log(value);
});

Of course, in this case the second console.log(value) does not get called due to the resolve addHierarchyLevelSelectionToDOM(value) failing. I would like to accomplish this goal in pure Javascript if possible.

Any assistance is greatly appreciated!

Upvotes: 2

Views: 96

Answers (3)

Rice
Rice

Reputation: 3531

The problem with the code was inside of selectValuesByFieldFromHierarchyTable

Inside of it was a call to an async function queryFeatures of type Deferred

The solution was to return the deferred (async) type and then the desired data array uniqueHierarchyLevels

function selectValuesByFieldFromHierarchyTable(currentlySelectedColumn) {
    //query database and return an array of strings
    //now returning the deferred type returned by queryFeatures as well as the array
     return hierarchyTableFeatureLayer.queryFeatures(hierarchyTableQuery, function(featureSet){

         for (var i = 0; i<featureSet.features.length; i++){
             uniqueHierarchyLevels.push(featureSet.features[i])
         }
     }).then(function afterQuery(){
            return uniqueHierarchyLevels;
        });
}

function populateHierarchyDefinitionFieldsFromSelectedHierarchyTable(){
    var selectedHierarchyDefinitionColumn = "COLUMN_NAME";
    deferred.resolve(selectUniqueValuesByFieldFromHierarchyTable(selectedHierarchyDefinitionColumn));
    deferred.then(function queryFeaturesAsyncCall(featureSetCallback) {
        featureSetCallback.then(
            function (hierarchyLevelsArray) {
                addHierarchyLevelSelectionToDOM(hierarchyLevelsArray);
            },
            function (err) {
            // Do something when the process errors out
                 console.log(err);
            })
       });
}

Upvotes: 0

frontend_dev
frontend_dev

Reputation: 1719

At least with your first - deleted - question it was likely that you had an error within the Promise constructor, more precisely in selectValuesByFieldFromHierarchyTable

Just do:

var p1 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve("bla");
  }, 2000);
});
p1.then(function(value) {
  console.log(value);
});

And suddenly it works. So this is the reason why you also should have a reject function in most cases, because reject() is not only called when you manually reject, but also when an error is thrown - for whatever reason:

p1.then(
  function resolve(value) {
      console.log(value);
  },
  function reject(error) {
      console.error(error);
  }
);

But wait! Now, if you have an error within "resolve" it will silently fail as well. So its even better to use this pattern:

p1.then(
  function resolve(value) {
      console.log(value);
  }
).catch(
  function reject(error) {
      console.error(error);
  }
);

Try again with this and the picture should become more clear.

(Note that the function naming is not mandatory but helps with debugging)

Edit: about "pure Javascript". Well, what do you mean? That is pure Javascript and Promises are a standard as well. Most modern Browsers can do this natively, and for the rest, just use a polyfill that should work perfectly, as Promises can be "emulated" 100%.

Upvotes: 3

Try this:

var p1 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve(selectValuesByFieldFromHierarchyTable(selectedHierarchyDefinitionColumn.innerHTML))
  }, 2000);
});
p1.then(addHierarchyLevelSelectionToDOM);

Upvotes: 1

Related Questions