简体   繁体   中英

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;

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;
}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM