Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js returning a promise from a function

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;
like image 885
Bosiwow Avatar asked Apr 04 '18 16:04

Bosiwow


People also ask

How do I return a promise from a function?

Promise resolve() method: If the value is a promise then promise is returned. If the value has a “then” attached to the promise, then the returned promise will follow that “then” to till the final state. The promise fulfilled with its value will be returned.

How do I return a promise in node?

The promise is resolved by calling resolve() if the promise is fulfilled, and rejected by calling reject() if it can't be fulfilled. Both resolve() and reject() takes one argument - boolean , string , number , array , or an object .

How do I return a promise from async function?

Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise. Note: Even though the return value of an async function behaves as if it's wrapped in a Promise.resolve , they are not equivalent.

How do you get a value returned by a promise?

Use the Promise. then() method to access the value of a promise, e.g. p. then(value => console. log(value)) .


1 Answers

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;
}
like image 68
t.niese Avatar answered Sep 30 '22 05:09

t.niese