0

How can I make my db queries synchronous with with my promise base function? In my code, I am running 3 db operations inside 3 different functions which need to be run in order, like waterfall model. The functions are running in waterfall model but db queries inside those functions are working in asynchronous. I need to run db queries inside those function in synchronous.

In this example I am expecting in console:

1
2
3
4

But I am getting

1
3
2
4

Code:

const Promise = require('bluebird');

// DB Settings    
const dbConfig = {
    user: process.env.DBUSER,
    password: process.env.DBPWD,
    database: process.env.DBNAME,
    host: process.env.DBHOST,
    port: process.env.DBPORT,
    poolSize: 10, // max number of clients in the pool
    //poolIdleTimeout: 30000, // how long a client is allowed to remain idle before being closed
    //reapIntervalMillis: 1000 //frequency to check for idle clients within the client pool
};
const pgp = require('pg-promise')();
const db = pgp(dbConfig);

var currentStatus = '',newStatus = '';

const _updateCurrentStatus = () => new Promise((resolve, reject) => {


    const _getCurrentStatus = (_userId) => new Promise((_resolve, _reject) => {        

        console.log("1");
        let statusQuery = "SELECT status FROM users WHERE id=" + _userId;

        db.one(statusQuery).then(function (data) {
            console.log("2");
            currentStatus = data.status;
            _resolve();
        }).catch(function (error) {
            _reject(error);
        });
    });

    const _setUpdateStatus = (cStatus, nStatus) => new Promise((_resolve, _reject) => {

        if(allApproved){
            if(cStatus == 'nvd_pending'){
                //nStatus = 'finance_pending';
                newStatus = 'finance_pending';
            }else if(cStatus == 'finance_pending'){
                //nStatus = 'oracle_pending';
                newStatus = 'oracle_pending';
            }else if(cStatus == 'oracle_pending'){
                //nStatus = 'active';
                newStatus = 'active';
            }else{
                //nStatus = cStatus;
                newStatus = cStatus;
            }
        }else{
            //nStatus = 'nvd_pending';
            newStatus = 'nvd_pending';
        }
        //_resolve(nStatus);
        _resolve();

    });

    const _updateStatus = (_newStatus, _payLoad) => new Promise((_resolve, _reject) => {

        console.log("3");

        let updateuserQuery = "UPDATE users SET status = '"+ _newStatus + "' WHERE id=" + _payLoad.user_id;
        let updatePanQuery = "UPDATE user_documents SET status = '" + _payLoad.panstatus + "' WHERE id= " + _payLoad.panid + " AND user_id=" + _payLoad.user_id;
        let updateFinanceQuery = "UPDATE user_finance_details SET status = '" + _payLoad.financestatus +" 'WHERE id= " + _payLoad.financeid + " AND user_id=" + _payLoad.user_id;

        db.tx(function (t) {
            console.log("4");
            // `t` and `this` here are the same;
            // this.ctx = transaction config + state context;
            return t.batch([
                t.none(updateuserQuery),
                t.none(updatePanQuery),
                t.none(updateFinanceQuery)
            ]);
        }).then(function (data) {
            _resolve(data);
        }).catch(function (error) {
            _reject(error);
        });


    });

    _getCurrentStatus(payLoad.user_id)
    .then(_setUpdateStatus)
    .then( _updateStatus(newStatus, payLoad))
    .then( values => {
            resolve(values);
        },error => {
            reject(error);
    })
    .catch((error) => reject(error));
});
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
pkd
  • 504
  • 5
  • 13
  • 3
    When you say "synchronous" do you mean "serially?" You just need to chain them together with `.then()`. You cannot run promises synchronously. – Jacob Jan 08 '17 at 07:54
  • yes, I mean chain. In this current example I wan to get current status of user, based on current status ,user will have new status, and then status will be updated, the issue is db part. It makes the thing async,and my promise function returns without waiting for db result. – pkd Jan 08 '17 at 08:05
  • 1
    Off-topic, but important nevertheless: You're building SQL by string concatenation instead of using parameterized queries. Your code might therefore be vulnerable to SQL injection attacks. I'm not a PostgreSQL expert, but perhaps take a look at [`PREPARE`d statements](https://www.postgresql.org/docs/9.2/static/sql-prepare.html) to resolve this issue. – stakx - no longer contributing Jan 08 '17 at 08:11
  • stakx, thnx for pointing. To overcome this I am already validating my inputs using regex. If you have any more points please suggest. – pkd Jan 08 '17 at 09:13

1 Answers1

2

You are overcomplicating thing here. First feedback is that you don't need those new Promise wrappers, since pg-promise is already creating promises. You can greatly flatten things here:

function getCurrentStatus(userId) {
  console.log("1");
  let statusQuery = "SELECT status FROM users WHERE id=" + userId;

  return db.one(statusQuery).then(function (data) {               
    console.log("2");
    return data.status;
  });      
}

function getUpdatedStatus(cStatus) 
  console.log('2');

  if (allApproved) {
    if(cStatus == 'nvd_pending'){
      newStatus = 'finance_pending';
    } else if (cStatus == 'finance_pending'){
      newStatus = 'oracle_pending';
    } else if (cStatus == 'oracle_pending'){
      newStatus = 'active';
    } else {
       newStatus = cStatus;
    }
  } else {
    newStatus = 'nvd_pending';
  }
  return newStatus;
}

function updateStatus(newStatus, payLoad) {
    console.log("3");

    let updateuserQuery = "UPDATE users SET status = '"+ newStatus + "' WHERE id=" + payLoad.user_id;
    let updatePanQuery = "UPDATE user_documents SET status = '" + payLoad.panstatus + "' WHERE id= " + payLoad.panid + " AND user_id=" + payLoad.user_id;
    let updateFinanceQuery = "UPDATE user_finance_details SET status = '" + payLoad.financestatus +" 'WHERE id= " + payLoad.financeid + " AND user_id=" + payLoad.user_id;

    return db.tx(function (t) {
        console.log("4");
        // `t` and `this` here are the same;
        // this.ctx = transaction config + state context;
        return t.batch([
            t.none(updateuserQuery),
            t.none(updatePanQuery),
            t.none(updateFinanceQuery)
        ]);
    });
});

function updateCurrentStatus(payLoad) {
  return getCurrentStatus(payLoad.user_id)
    .then(cStatus => getUpdatedStatus(cStatus))
    .then(newStatus => updateStatus(newStatus, payLoad));
}

The specific reason you're seeing 3 out of order is because you're calling it immediately via _updateStatus(newStatus, payLoad) instead of wrapping it in a function (see my suggested code updates above).

Jacob
  • 77,566
  • 24
  • 149
  • 228
  • Thnx Jacob. Your solution solved my issue. Earlier I used to async module,But right now I am working to build a microservice based solution where each service codebase will have to be max 4MB size and async and dependent modules it self takes approx 4MB. can you suggest me where I can have better guide for promise base architecture. – pkd Jan 08 '17 at 09:11
  • Thanks man, good answer ;) It's been a tiring fight for me, tutoring proper use of `pg-promise` to developers who do not understand promises well. – vitaly-t Jan 08 '17 at 16:50