0

I am using node 10+, and I have this function where I do a database query and wait for the result and return it:

var test3 = (req,res,query) => {   
    var conn = new sql.ConnectionPool(dbconfig);
    var req = new sql.Request(conn);
    var result;
    return  conn.connect().then(async() => {       
            result = await req.query(query);               
            conn.close();
            return result;

           }).catch(e => {
                return e;
           }).finally(() => {
                conn.close();
           });   
}

First, I would like to know why I have to return the conn.connect() block..

return  conn.connect().then(async() => {...  

I know it has something to do with promise chaining I think, but I dont understand why, because my async db call is already resolved from the await dbcall function... and I just return the result from inside the function

Then, I have a router where I call the api function here:

router.get("/api/compareCount",  function(req,res) {
    var query = `SELECT COUNT(*) as count
      FROM [DublettenReferenzmenge].[dbo].[DocumentForElasticsearch] where lastChange < dateadd(day,-1,getdate())`;
      var query2 = `SELECT COUNT(*) as count
      FROM [DublettenReferenzmenge].[dbo].[DocumentForElasticsearch] where lastChange < dateadd(hour,-8,getdate())`;
      var query3 =`SELECT COUNT(*) as count
      FROM [DublettenReferenzmenge].[dbo].[DocumentForElasticsearch]`;
    axios.all([searchES(req,res),   test3(req,res,query),   test3(req,res,query2) , test3(req,res,query3)])
    .then(axios.spread(function (esCount, mssqlCount1, mssqlCount2, mssqlCount3) {
        totalES = esCount.hits.total;

        totalMSSQL  = mssqlCount1.recordset[0].count;
        totalMSSQL2 = mssqlCount2.recordset[0].count;
        totalMSSQL3 = mssqlCount3.recordset[0].count;totalMSSQL,  " mssqlCount2: ", totalMSSQL2, "mssqlCount3: ", totalMSSQL3);
        var msg = "ES Dokumente total: " + totalES + " MSSQL Dokumente total: " + totalMSSQL + "<br>";
        if ( totalES != totalMSSQL) {
            msg += "Critical: " + totalES != totalMSSQL + "<br>"; 
        } if ((totalES != totalMSSQL2))  {            
            msg += "Warning: " + (totalES != totalMSSQL2) + "<br>";
        } if ((totalES > totalMSSQL3))  {  
            msg += "Achtung es gibt ungelöschte Dokumente im Elasticsearch Index!";
        }      
        res.set('Content-Type', 'text/html');
        res.send(msg);
    })).catch((err) => {
        res.send(err);
    });
})

router.get("/api/test3", async function (req,res) {
    var query = `SELECT COUNT(*) as count
      FROM [DublettenReferenzmenge].[dbo].[DocumentForElasticsearch] where lastChange < dateadd(day,-1,getdate())`;
    var result = await test3(req,res,query);
    res.json(result);
})

The api/test3 route returns me the result as usual, but the api/compareCount does return me correct results as well...

Furthermore, I have to use the async function ... await test3(..) async-await syntax structure to resolve my result into a variable... But I do not have to use that same structure for my api/compareCount function above, the result is returned anyways in the .then(axios.spread(function(...))). Why is that? I am quite confused as I don't really know the inner workings of the Promise chaining and calls...

EDIT: before my test3() function, I had something like this:

async function testQuery(query) {
    try {
        let pool = await sql.connect(dbconfig);
        let result1 = await pool.request()
            //.input('input_parameter', sql.Int, value)
            .query(query);  
            sql.close();  
        return result1;      

    } catch (err) {
        console.log(err);
        sql.close();  

    } finally {
        sql.close();
    }
};

I got also results with that function, however, I got some kind of race condition where it told me that the sql- connection already exists, and do sql.close() first if I reload the page too quickly... I dont get this with the test3() function anymore...

MMMM
  • 3,320
  • 8
  • 43
  • 80
  • 1
    If you think through the actual timing of things, the `return connection.connect()` happens BEFORE your `await` is done. If you don't return the promise, then the outside world has no way of knowing when any of the async stuff in that function is done or what its result is. Functions don't block to the outside world just because they have `await` in them. `await` only affects the flow inside the function. It doesn't block the actual return from the function at all. – jfriend00 Feb 07 '19 at 08:48
  • ok but what does it mean now? ;) OK, didn't know exactly what return connection.connect() does, so it returns to the outside (or inside my other function where it gets called), and kind of replaces the inside code with the await part, right? So this await part then is kind of blocking and only returning when results are coming in, but thats no problem I think because thats just the default-behaviour, right? :) So everything is fine in this case I think... ? I would only be bothered if somehow I didnt manage sql connections right, e.g if there are many connections left open or so eating memory – MMMM Feb 07 '19 at 09:21
  • I left this as a comment, not an answer, because you need to learn a lot about how asynchronous operations work in Javascript and I'm headed off to sleep now without enough time to write a tutorial on the topic. Perhaps some more reading on your part about asynchronous operations in general and about how await works in Javascript would help. – jfriend00 Feb 07 '19 at 09:29
  • I know the basics how asynchronous operations work and how async await works.. behind the scenes, it always returns a new Promise() and await resolves that promise... but it doesn't help somehow... – MMMM Feb 07 '19 at 10:02
  • [Don't mix `await` and `then` syntax](https://stackoverflow.com/a/54387912/1048572)! – Bergi Feb 07 '19 at 22:36

1 Answers1

1

To start with, test3() needs to return a promise so the caller knows when it's done and whether it had an error or not. It's probably easiest to use async/await for that:

async function test3(query) => {   
    const conn = new sql.ConnectionPool(dbconfig);
    const request = new sql.Request(conn);
    await conn.connect();
    try {
        const result = await request.query(query);
        return result;
    } finally {
        conn.close();
    }
}

Various changes:

  1. Only pass arguments that are going to be used
  2. Use await to simplify async logic flow
  3. Use try/finally to catch any error after connected so we can always close the connection and not leak a connection
  4. Switch from var to const.
  5. Make function async so it returns a promise that is hooked to when the internals are done or have an error

Then, if you adjust how test3() is called to only pass the query argument (since that's all that is used), your use of that function the other places you are using it should work.

Your code was doing this:

var result = await test3(req,res,query);

But, test3() had no return value so the await had nothing useful to do. await works with a promise and you weren't returning a promise that was linked to the async operations inside of test3(). That's what my changes above do.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • ok thanks, but why are you doing "await conn.connect()" after the request? I thought it should be before because it builds up the connection, or is it just a request object? But I think this makes sense to me, its nice clean code :) – MMMM Feb 08 '19 at 18:38
  • @user2883596 - That sequence of operations was just copied from your code. I don't know your db. – jfriend00 Feb 08 '19 at 19:06
  • @user2883596 - Also, the query itself is AFTER the `await conn.connect()`. Presumably, `const request = new sql.Request(conn);` is just creating a request object. – jfriend00 Feb 08 '19 at 19:43