Node.js 从函数返回一个承诺

Posted

技术标签:

【中文标题】Node.js 从函数返回一个承诺【英文标题】:Node.js returning a promise from a function 【发布时间】:2018-09-14 06:50:15 【问题描述】:

我正在探索 node.js 中承诺和回调的可能性 我正在尝试找到一种使此代码起作用的方法。目前我面临的问题是,当我调用一个函数并想要使用返回值时,它还没有准备好。我知道我必须做什么,但不知道怎么做。基本上,我必须让 insertAddress() 返回一个承诺(这样我就可以在上面使用 .then() ),或者将回调作为参数。为此,我还认为 databaseWork() 应该返回一个承诺。但我不知道在哪里添加它。 问题位于“console.log(out)”中,它在设置 out 变量之前运行(因为 insertAddress 仍在运行)。 这是我的代码

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;

【问题讨论】:

【参考方案1】:

一种方法是使用异步等待。

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

这里是 npm 模块:https://www.npmjs.com/package/async

【讨论】:

【参考方案2】:

你可以在你的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();
      
    );
  )


databaseWork 返回的Promise 将由workload 的结果解析。现在您可以将insertAddress 更改为:

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

然后您需要将路线更改为:

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 代码,带有返回 Promise 的 getConnection 函数:

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;

【讨论】:

@Bosiwow 但请注意错误处理。这个解决方案仍然需要一些工作,因为我没有修复我们代码中的其他缺陷。你不检查getConnection 是否有错误。此外,如果workload 失败,您可能需要在 catch 块中添加rollback。这段代码只是为了给你一个如何解决它的一般想法。您甚至可以考虑创建 getConnection 的异步版本,以使您的 databaseWork 更清晰。 我不能把'dbpool.getConnection(async (err, connection) => '放在try块里面吗?我可以在catch块里面做回滚吗?如果里面回滚失败怎么办? catch 块。这不好,对吧?finally 块中的释放怎么办。如果失败了怎么办? @Bosiwow 不,你不能将它放在 try catch 块中 dbpool.getConnection 不会因抛出异常而失败,但会将错误传递给回调。我用返回承诺的getConnection 更新了问题。如果getConnection 被拒绝,databaseWork 将不会被拒绝。对于您的其余问题。这超出了您原始问题的范围。如果您在 SO 上没有找到关于该问题的答案,那么您可能会提出一个新问题。 您好,非常感谢您的回复。您说“如果 getConnection 被拒绝,则不会拒绝 databaseWork。”。如果我在 getConnection() 被拒绝时理解正确。不会有连接对象。因此“await beginTransaction(connection)”也将被拒绝。但这不会触发捕获,不是吗?编辑:好的,当一个承诺被拒绝时,cath 被称为developer.mozilla.org/en-US/docs/Web/javascript/Reference/… @Bosiwow 抱歉,打错了。现在使用await,如果getConnectiondatabaseWork 将被拒绝,现在您可以将await getConnection 放在try 块中,或者您让databaseWork 失败并出现getConnection 的错误。

以上是关于Node.js 从函数返回一个承诺的主要内容,如果未能解决你的问题,请参考以下文章

在 node.js 中承诺一个递归函数

Node.js 承诺请求返回

如何在 node.js 中解决可变数量的承诺

Node.js / Javascript 多重递归承诺在视图中返回

无法通过递归调用node.js中的函数从promises获得响应

带有 Q 承诺的 Node.JS 猫鼬请求返回空