3

I have been using JavaScript for about a year and a half now, but I still have a fundamental misunderstanding of Promises and can never get them to work.

Can someone tell me what is wrong with this code and why it doesn't wait for the promise to fulfill?

let sitesGeo = [];
let p = new Promise((resolve, reject) => {
     sitesGeo = getSitesGeo(currentBatch, sitesUrl, sitesGeo);
     resolve(sitesGeo);
 }).then((sitesGeo) => {
      console.log("sites geometry ", sitesGeo);
  });

I keep getting undefined for "sites geometry" and I know it's because it is being logged before the promise gets resolved. I know that the function getSitesGeo works because I have another log statement inside of it and it returns the result.

What am I getting wrong, and how to I get console.log statement to wait for the promise to resolve before executing?

Here is the code so maybe this will help to understand why it is not working;

function getSitesGeo(currentBatch, sitesUrl) {
    console.log("getting sites geometry");
    var sitesGeoArray = [];
    var queryTask = new QueryTask(sitesUrl);
    var batchQuery = new Query();
    batchQuery.where = "Batch = " + currentBatch;
    batchQuery.outFields = ["*"];
    batchQuery.returnGeometry = true;
    queryTask.execute(batchQuery).then(function(result) {
        console.log("sites geometry ", result);
        let features = result.features;
        features.forEach(function(feature) {
            var geometry = feature.geometry;
            if(isNaN(parseFloat(geometry.x)) === false) {
                sitesGeoArray.push(geometry);
            }
        })
        console.log("sites array ", sitesGeoArray);
        console.log("geometry type = ", typeof(sitesGeoArray));
        return sitesGeoArray;
    });
}

function getBatchNumnber() {
    var sitesUrl = 
    "https://ocean.floridamarine.org/arcgis/rest/services/.../MapServer/0";
    var queryTask = new QueryTask(sitesUrl);
    var batchQuery = new Query();
    var statDef = new StatisticDefinition();
    statDef.statisticType = "max";
    statDef.onStatisticField = "Batch";
    statDef.outStatisticsFieldName = "CurrentBatch";
    batchQuery.where = "1=1";
    batchQuery.outFields = ["*"];
    batchQuery.outStatistics = [statDef];
    batchQuery.returnGeometry = true;
    queryTask.execute(batchQuery).then((result) => {
        console.log("batch query result ", result);
        var currentBatch = result.features[0].attributes.MAX_Batch;
        window.currentBatch = currentBatch;
        //var sitesGeoArray = [];
        async function createSitesGeoArray(currentBatch, sitesUrl) {
            return new Promise((resolve, reject) => {
                const sitesGeo = await getSitesGeo(currentBatch, sitesUrl);
                resolve (sitesGeo);
            });
        }
        createSitesGeoArray(currentBatch, sitesUrl)
        .then(result => {
            console.log("sites geometry = ", result);  
        })

I re-wrote the async function to :

async function createSitesGeoArray(currentBatch, sitesUrl) {
            let promise = new Promise((resolve, reject) => {
                const sitesGeo = getSitesGeo(currentBatch, sitesUrl);
                resolve (sitesGeo);
            });
            let result = await promise;
            //return result;
        }
        createSitesGeoArray(currentBatch, sitesUrl)
        .then(result => {
            console.log("sites geometry = ", result);
        })

I don't get any errors, result is now undefined (not waiting to resolve)

  • First of all your getSitesGeo function doesn't return anything. It must return a promise. – dmh126 May 3 at 20:12
  • I fixed this code, look at my edited answer. Let me know if it's okay. – dmh126 May 3 at 20:17
2

Well, I think that the getSitesGeo function is asynchronous and that's why you've got undefined, because you resolve this variable before it finish to fetch everything. Try to wrap your code like that:

async function grabData(currentBatch, sitesUrl, sitesGeo) {

    return new Promise( (resolve, reject) => {
        const sitesGeo = await getSitesGeo(currentBatch, sitesUrl, sitesGeo);
        resolve(sitesGeo);
    });

}

And now you can use then and catch on the function:

grabData(currentBatch, sitesUrl, sitesGeo)
.then( sitesGeo => {
    console.log(sitesGeo);
})

You can see these async/await keywords, the first means that we create a function which has some asynchronous code inside and await just indicates that here we are waiting for the response. Your code doesn't wait for anything there, before you grab data from getSitesGeo it prints the variable which is undefined.

More about that: https://javascript.info/async-await

It is quite convenient to write code like that, other option is using callbacks instead of promises but it can lead to very ugly code (callback hell).

Async library is also a really good module to handle async requests, especially multiple requests etc.: https://caolan.github.io/async/

Edit: I fixed your code and now it should work.

function getSitesGeo(currentBatch, sitesUrl) {
    console.log("getting sites geometry");
    var sitesGeoArray = [];
    var queryTask = new QueryTask(sitesUrl);
    var batchQuery = new Query();
    batchQuery.where = "Batch = " + currentBatch;
    batchQuery.outFields = ["*"];
    batchQuery.returnGeometry = true;
    return new Promise( (resolve, reject) => {
        queryTask.execute(batchQuery).then(function(result) {
            console.log("sites geometry ", result);
            let features = result.features;
            features.forEach(function(feature) {
                var geometry = feature.geometry;
                if(isNaN(parseFloat(geometry.x)) === false) {
                    sitesGeoArray.push(geometry);
                }
            })
            console.log("sites array ", sitesGeoArray);
            console.log("geometry type = ", typeof(sitesGeoArray));
            resolve(sitesGeoArray);
        });
    }
}

function getBatchNumnber() {
    var sitesUrl = 
    "https://ocean.floridamarine.org/arcgis/rest/services/.../MapServer/0";
    var queryTask = new QueryTask(sitesUrl);
    var batchQuery = new Query();
    var statDef = new StatisticDefinition();
    statDef.statisticType = "max";
    statDef.onStatisticField = "Batch";
    statDef.outStatisticsFieldName = "CurrentBatch";
    batchQuery.where = "1=1";
    batchQuery.outFields = ["*"];
    batchQuery.outStatistics = [statDef];
    batchQuery.returnGeometry = true;
    queryTask.execute(batchQuery).then((result) => {
        console.log("batch query result ", result);
        var currentBatch = result.features[0].attributes.MAX_Batch;
        window.currentBatch = currentBatch;
        //var sitesGeoArray = [];
        getSitesGeo(currentBatch, sitesUrl)
        .then(result => {
            console.log("sites geometry = ", result);  
        })
    })
}
  • I have tried turning the getSitesGeo() function into a promise, but it doesn't seem to matter, I get undefined, so currently it is an async function. I also tried the async/await method as you described above and as you can see in my code, but I am now getting the error: 'Uncaught SyntaxError: await is only valid in async function'. So why would the function createSitesGeoArray(currentBatch, sitesUrl) not be an async function? Is there a syntax error I am not seeing? – GeoGene May 3 at 14:19
  • You can use await only in async function. There is a keyword async before grabData. Without it you're not able to use await inside this function. – dmh126 May 3 at 14:35
  • Yes, I did have the async keyword before the createSitesGeoArray() function but was still getting the error. I ended up re-writing the function, and now I don't get the error any more, but it still isn't waiting on the function to resolved before logging the results. I still get undefined. – GeoGene May 3 at 14:52
  • That worked! Thank you. The devil is in the details :) – GeoGene May 6 at 18:49
4

It looks like the function you're using, getSitesGeo(), probably returns a promise. If so, you do not need to mess with the new Promise(...) stuff - just call .then() on your promise (That is returned from the function call) directly.

var getSitesGeoPromise = getSitesGeo(currentBatch, sitesUrl, sitesGeo);

getSitesGeoPromise.then(function(sitesGeo) {
  // this function will get called if there is a success, and the results will 
  // be in the variable "sitesGeo"
  console.log("sites geometry ", sitesGeo);
}, function(err) {
  // this second function will get called if there is an error
  console.error('Error getting sites geometry');
});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.