1

Note to would-be closers or markers-as-duplicate : this question is not answered in How do I convert an existing callback API to promises?, as all answers there treat of calls in isolation and do not explain how to deal with successive, dependent calls.

This question is basically the same as in combining results from several async/await calls, but because of my slightly different context I fail to see how the solution therein can be adapted/mimicked.

I have two successive calls to a database, using an old API which only knows about callbacks. The second call needs objects/values returned by the first.

I have a working callback-version of the code, as follows :

connection.query(sql1,function(error1,results1,fields1) {
    if(error1) {
         console.log("Error during first query",error1);
    }
    let sql2=computeUsingFirstResult(result1);

    connection.query(sql2,function(error2,results2,fields2) {
      if(error2) {
           console.log("Error during second query",error2);
       }
       doSomething(connection,results1,results2);
      })  
});

Here is my unsuccessful attempt to do it in async/await-style :

const util = require('util');
async function firstQuery(connection) {
    return util.promisify(connection.query).call(sql1).catch(
        error1 => console.log("Error during first query : ", error1)
    );;
}

async function secondQuery(connection, result1) {
    let sql2 = computeUsingFirstResult(result1);
    return util.promisify(connection.query).call(sql2).catch(
        error2 => console.log("Error during second query : ", error2)
    );
}

let finalResult = {};

async function main() {
    const results1 = await firstQuery(connection);
    const results2 = await secondQuery(connection, results1);
    doSomething(connection, results1, results2);
    finalResult = [results1,results2];
    console.log("Here is the finalResult : ", finalResult);
}

main().catch(
    err => console.log("Something went wrong towards the end", err)
);



My async/await version fails as all the intermediate results are undefined. Yet, as far as I can see it should be equivalent to the non-async version above.

What is the correct way to do this ?

Liam
  • 27,717
  • 28
  • 128
  • 190
Ewan Delanoy
  • 1,276
  • 2
  • 13
  • 31

1 Answers1

1

There are 2 approaches I am hoping you can try and see if either of them help:

(1) Bind the util.promisify to connection for both firstQuery and secondQuery something like so:

async function firstQuery(connection) {
    return util.promisify(connection.query).bind(connection,sql1)().catch(
        error1 => console.log("Error during first query : ", error1)
    );;
}

// bind makes sure that internal bindings of connection object are in tact

If the above approach doesn't yield any result, try the next approach:

(2) Try using Promises-Wrapper instead of util.promisify for both firstQuery and secondQuery like so:

function firstQuery(connection,sql1){
  return new Promise((resolve,reject)=>{
    connection.query(sql1,function(error1,results1,fields1){
      return error1 ? reject(error1):resolve(results1);
    })
  });
}

And now call them as you were in your code using Async/Await in the main function.

Few Potential bugs/typos I noticed in the code:

(1) firstQuery doesn't seem to be passed sql1 as one of its arguments, maybe this is intentional if sql1 exists in global scope.

(2) If you attach a catch block to both your queries, they will have an impact on your 2 calls (see comments in the code below):

async function main() {
    const results1 = await firstQuery(connection); //<-- if this call fails, results1 will be undefined and the call will go to next step
    const results2 = await secondQuery(connection, results1); // same thing here
    doSomething(connection, results1, results2);
    finalResult = [results1,results2];
    console.log("Here is the finalResult : ", finalResult);
}

A possible solution is to remove the catch blocks from individual functions and just return the respective promises.

Then you can deal with them via a single try/catch block like this:

async function main() {
    try{
      const results1 = await firstQuery(connection);
      const results2 = await secondQuery(connection, results1);
      doSomething(connection, results1, results2);
      finalResult = [results1,results2];
      console.log("Here is the finalResult : ", finalResult);
   }catch(err){
      console.log("Error",err);
   }
}

This will ensure that as soon as the first call fails, function goes straight to catch block without executing any other lines.

nishkaush
  • 1,512
  • 1
  • 13
  • 20