0

So I have:

async function functionName() {
  return await new Promise( async (resolve, reject) => {
    try { 
      //variables and such
      for (let i = 0; i < results.length; i++) {
        await geo.geocode('mapbox.places', address, async function (err, geoData) {
          await db.query(`INSERT INTO table (col1, col2, col3) VALUES($1, $2, $3) ON CONFLICT DO NOTHING`, ["default", val2, val3], (err, res) => {
            if (err) {
              console.error(err);
              return;
            }
          }
        }
      }
      resolve("resolved")
    } catch(e) {
      reject(err)
    }
  }
}

For some reason, when I call this function, it seems like the function is resolving but the queries are made to the database AFTER the resolving is completed. How do I make sure the function only resolves once the queries are completed/the database is updated?

nickcoding2
  • 142
  • 1
  • 8
  • 34

1 Answers1

2

You can't mix plain callbacks with promises and have any hope in controlling the flow. Your functions such as geo.geocode() and db.query() do not return a promise when you pass them a callback and thus await does nothing useful. Instead, you need to use the promise version of those functions or create a promisified version if one doesn't already exist.

If you use the beta version of the geocode library that supports promises and you use a version of your sql library that supports promises, then you would write your code like this:

async function functionName() {
      //variables and such
      for (let i = 0; i < results.length; i++) {
        let geoData = await geo.geocode('mapbox.places', address);
        await db.query(`INSERT INTO table (col1, col2, col3) VALUES($1, $2, $3) ON CONFLICT DO NOTHING`, ["default", val2, val3]);
      }
}

But, since this sql statement is the same no matter what iteration of the loop you're doing, there must be some code missing here that uses the geoData.


If you don't have access to the versions of geo.geocode and db.query that support promises, then you can manually promisify individual methods yourself like this:

const util = require('util');
const geo.geocodeP = util.promisify(geo.geocode);
const db.queryP = util.promisify(db.query);

async function functionName() {
    //variables and such
    for (let i = 0; i < results.length; i++) {
        let geoData = await geo.geocodeP('mapbox.places', address);
        await db.queryP(`INSERT INTO table (col1, col2, col3) VALUES($1, $2, $3) ON CONFLICT DO NOTHING`, ["default",
            val2, val3
        ])
    }
}

Keep in mind that none of this makes the function "wait" before returning. The function will still return a promise immediately when it hits the first await and the caller will have to use that promise with await or .then() to get the eventual results from the function. What this allows you to do though, is sequence your asynchronous operations in your for loop so they happen in sequence where one asynchronous operation waits for the prior to complete before the next runs.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Well yeah, the geodata gives you values that you put into the DB columns. I found this (https://github.com/vitaly-t/pg-promise) for the Postgres query functions...would that work you think? As for the .geocode() function, that's built into Mapbox and I don't see any promise versions--do you have a link for the beta? All I did was an npm install of mapbox-geocoding to get access to that function (and it seemed like a pretty old api) – nickcoding2 May 10 '21 at 03:16
  • 1
    @nickcoding2 - The beta version is mentioned [here](https://developers.google.com/maps/documentation/javascript/reference/geocoder). You can also use [`util.promisify()`](https://nodejs.org/api/util.html#util_util_promisify_original) to create a promisfied version of any nodejs calling style callback-based function. They key thing to remember with `await` is that it ONLY does something useful and what you want if it is awaiting a promise that is tied to your asynchronous operation. awaiting something that does not return a promise does nothing. – jfriend00 May 10 '21 at 03:21
  • Hi, thank you so much for all of your help, but I'm using Mapbox. So I probably have to make promisified functions of both the geocode function and the query function, right? Also after doing some quick research into promisified functions using util.promisify(), what exactly should it all look like? I looked at the link you attached and am trying to figure out the .then and catch version and how it would help me...Would I put the promisified query (with its .then and catch) inside the .then block of the promisified geocode function? – nickcoding2 May 10 '21 at 03:38
  • @nickcoding2 - OK, I added additional examples to my answer that show how you can manually promisify your asynchronous methods so you can then use `await` with those promise-returning methods. – jfriend00 May 10 '21 at 05:36