5

I'm exploring the possibilities of promises and callbacks in node.js I'm trying to find a way for this code to work. Currently the issue I'm facing is that when I'm calling a function and want to use the return value, it is not ready yet. I know what I have to do, but don't know how. Basically, I have to make that insertAddress() returns a promise (so I can use the .then() on it), or takes a callback as a param. To do this, I also think databaseWork() should return a promise. But I don't know where to add it. The issue is located in the 'console.log(out)', that runs before out variable is set (because insertAddress is still running). Here is my code

app.js
-----

const databaseWork = require('./db/mysql.js').databaseWork;

app.use('/test',  (req, resp) => {
  var address = {
     country : "Country",
     city : "Randomcity",
     street : "Random",
     number : 6,
     postalcode : "A789",
     province : "a province"
   }
  var out = insertAddress(address);  //<== takes time to finish, is not ready when the next console.log finishes
  console.log(out);
});

function insertAddress(address){
    var rows
    databaseWork(
      //Following anonymous function contains the actual workload. That has to be done inside a transaction
       async (connection) => {
       rows = await insertAddressQuery(address,connection);
       console.log(rows); //this one waits for insertAddressQuery to be complete
    })
    return rows; //this will run before insertAddressQuery is complete
}


function insertAddressQuery(address,connection) {
    return new Promise( (resolve, reject) => {
    //async job
      connection.query('INSERT INTO address (country,city,Street,number,postalcode,province) VALUES(?,?,?,?,?,?)', [address.country,'4','5',6,'7','8'] , (err, rows) => {
            if (err) {reject(err);}
              resolve(rows);
        });
    });
};


/db/mysql.js
------------

var mysql = require('mysql');
var dbpool = mysql.createPool({
  host: process.env.HOST_DB,
  user: process.env.USER_DB,
  password: process.env.PWD_DB,
  database: process.env.DB
});

function databaseWork(workload){
  dbpool.getConnection( async (err, connection) => {
      await beginTransaction(connection);

      await workload(connection);

      await commitTransaction(connection)
      connection.release();
    });
}


function beginTransaction(connection){
  return new Promise( (resolve, reject) => {
    //async job
    connection.beginTransaction( (err) => {
      if (err) {reject(err);}
        resolve();
    });
  });
};



function commitTransaction(connection) {
    return new Promise( (resolve, reject) => {
    //async job
    connection.commit( (err) => {
        if (err) {reject(err);}
          resolve();
        });
    });
};

exports.databaseWork = databaseWork;

2 Answers 2

6

You would do that in your databaseWork:

function databaseWork(workload) {
  return new Promise((resolve, reject) => {
    dbpool.getConnection(async (err, connection) => {
      try {
        await beginTransaction(connection);

        var result = await workload(connection);

        await commitTransaction(connection)

        resolve(result);
      } catch( err ) {
         reject(err)
      } finally {
        connection.release();
      }
    });
  })

}

The Promise returned by databaseWork will be resolved by the result of workload. And now you can change insertAddress to this:

async function insertAddress(address){
    return databaseWork(connection => {
       return insertAddressQuery(address,connection);
    })
}

You then need to change the route to this:

app.use('/test', async (req, resp) => {
  var address = {
    country: "Country",
    city: "Randomcity",
    street: "Random",
    number: 6,
    postalcode: "A789",
    province: "a province"
  }
  var out = await insertAddress(address); // use await here to wait for insertAddress to be finished

  console.log(out);
});

*UPDATE code with an getConnection function that returns a Promise:

function getConnection() {
  return new Promise((resolve, reject) => {
    dbpool.getConnection((err, connection) => {
      if (err) {
        reject(err)
      } else {
        resolve(connection);
      }
    })
  });
}

async function databaseWork(workload) {
  var connection = await getConnection();
  var result;
  try {
    await beginTransaction(connection)

    result = await workload(connection)

    await commitTransaction(connection)
  } catch (err) {
    // a rollback might be neccesaary at that place
    throw err
  } finally {
    connection.release();
  }

  return result;
}
Sign up to request clarification or add additional context in comments.

6 Comments

@Bosiwow But take care of the error handling. This solution still needs some work, because I didn't fix the other flaws in our code. You don't check if getConnection has an error. And also if workload fails you might want to add a rollback in the catch block. This code is just to give you a general Idea how you can solve it. You might even consider to create an async version of getConnection to make your databaseWork clearer.
Can I not put the 'dbpool.getConnection(async (err, connection) => {' inside the try block? And can I do a rollback inside the catch block? What if the rollback fails inside the catch block. This isn't good, right? What about the release in the finally block. What if that fails?
@Bosiwow No you can't place it in try catch block dbpool.getConnection will not fail with an exception that is thrown, but will pass the error to the callback. I updated the question with a getConnection that returns a promise. The databaseWork would not be rejected if getConnection is rejected. To the rest of your questions. That's out of the scope of your original question. If you don't find an answer about that on SO, then you might open a new question.
Hello many thanks for your reply. You said 'The databaseWork would not be rejected if getConnection is rejected.'. If I understand correctly when getConnection() get rejected. There won't be a connection object. Hence "await beginTransaction(connection)" would also be rejected. But this doesn't trigger the catch, does it? EDIT: ok when a promise get's rejected the cath is called developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
@Bosiwow Sorry, that was a typo. Now with using await, The databaseWork would be rejected if getConnection , and now you could place the await getConnection in a try block, or you let databaseWork fail with the error of getConnection.
|
0

One way you can do this is by using async await.

var example = async (req, res) => {
    var response = await myAsyncTask();

    // this will get logged once the async task finished running.
    console.log(response)
}

// Use async await to get response
var myAsyncTask = async () => {
    try {
        var response = await asyncTaskINeedDataFrom()
        return response;
    }
    catch(err) {
        return console.log(err);
    }
}

Here's the npm module: https://www.npmjs.com/package/async

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.