简体   繁体   中英

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!

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%.

Try this:

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

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);
            })
       });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM