使用node.js中的async/await与MySQL结合使用

44

我需要使用async/await关键字对所有结果进行同步,并将它们追加到字符串中,就像在C#中一样。

我是初学者,不太适应在我的代码中使用这种新语法。

var string1 = '';
var string2 = '';
var string3 = '';
var string4 = '';

DatabasePool.getConnection(function(err, connection) {

        connection.query(query,function (err, result) {
            if (err){};
            string1 = result;
        });

        connection.query(query,function (err, result) {
            if (err){};
            string2 = result;
        });     

        connection.query(query,function (err, result) {
            if (err){};
            string3 = result;   
        });

        connection.query(query,function (err, result) {
            if (err){};
            string4 = result;
        }); 

       //I need to append all these strings to appended_text but  
       //all variables remain blank because below code runs first.
       var appended_text = string1 + string2 + string3 + string4;
});

请确认一下,您正在使用支持async/await的Node版本,对吗? - adam-beck
是的,最新版本。 - Burc Hasergin
12个回答

99

如果你正在使用 Node 8 或更高版本,你可以利用原生的 util.promisify() 方法来处理 node mysql。

别忘了使用 bind() 来调用它,这样 this 就不会出错:

const mysql = require('mysql'); // or use import if you use TS
const util = require('util');
const conn = mysql.createConnection({yourHOST/USER/PW/DB});

// node native promisify
const query = util.promisify(conn.query).bind(conn);

(async () => {
  try {
    const rows = await query('select count(*) as count from file_managed');
    console.log(rows);
  } finally {
    conn.end();
  }
})()

4
谢谢提醒要添加bind。我已经花费了太长时间来解决使用promisify时遇到的问题。 - Chris Owens
非常感谢您提供正确的绑定方法提示。 - fmquaglia
1
如果我必须向查询函数提供两个参数,TypeScript 会给出“预期 1 个参数”的错误。我使用两个参数是因为查询字符串包含一个 ?。有没有办法在单个查询字符串中避免附加第二个参数? - Amit Kumar
对于 TypeScript:const query: (arg1: string, arg2?: string[])=>Promise<unknown> = promisify(db.query).bind(db); - Amit Kumar
mysql-async-simple包可以为您完成这项工作。 - Hash
@AmitKumar,对于TS,你可以使用QueryOptions作为第一个参数,而不是传递queryString,它包含sqlvalues和其他选项。 - LeOn - Han Li

74

使用 mysql2 包。它有 Promise 封装,所以你可以这样做:

async function example1 () {
  const mysql = require('mysql2/promise');
  const conn = await mysql.createConnection({ database: test });
  let [rows, fields] = await conn.execute('select ?+? as sum', [2, 2]);
}

12
这是最优秀的答案...天哪Mysql2比mysql 快得多,并提供了大量mysql没有的功能,如承诺、压缩和预处理语句。 - Nick Steele
4
执行查询后,不要忘记使用 await conn.end() 来结束连接,或者更好的方式是使用连接池(如 createPool)而不是单个连接,并使用 end() 方法来结束连接池。 - Fuad All
1
这应该是被接受的答案,因为mysql现在已经被弃用,而mysql2仍然得到维护,并支持异步。 - Epic Speedy
我有点困惑,我需要为每个查询创建一个到MySQL的连接还是将conn作为全局变量并在所有路由中使用? - Ali Raza
1
https://npmcompare.com/compare/mysql,mysql2 --> mysql2 是一个更好的包,它还具有更有意义的错误信息,这个比较无法告诉您。 - garg10may

13
假设您正在使用基于 Promise 的 ORM,您可以这样做:

假设您正在使用基于 Promise 的ORM,您可以这样做

async function buildString() {
  try {
    const connection = await DatabasePool.getConnection();
    const string1 = await connection.query(query);
    const string2 = await connection.query(query);
    const string3 = await connection.query(query);
    const string4 = await connection.query(query);

    return string1 + string2 + string3 + string4;
  } catch (err) {
    // do something
  }
}

通过在调用前加上await,可以将任何承诺与async/await一起使用。但是,请注意,此函数必须在async函数“包装器”内使用。您需要在try/catch块中处理错误。

我还要指出,这4个查询不会同时运行。对于此操作仍需使用Promise.all。


1
请注意,await 只能在异步方法中使用。 - Wilco Bakker
1
谢谢@WilcoBakker。我实际上只是在编辑我的答案 :) - adam-beck
我使用mysql包。我认为这不是基于Promise的?我应该使用哪个mysql包? - Burc Hasergin
有相当多的选择,所以我认为你可能想花些时间来探索权衡。你可能会遇到一些选择,比如:objection.js、bookshelf、sequelize。 - adam-beck
如何获取查询返回的行值而不是字符串? - Burc Hasergin

6

如果你想使用mysql(也称为mysqljs),如果不想使用包装器,就需要进行一些工作。但这很容易。下面是connect函数的示例:

const mysql = require('mysql')

var my_connection = mysql.createConnection({ ... })

async function connect()
{
    try
    {
        await new Promise((resolve, reject) => {
            my_connection.connect(err => {
                return err ? reject(err) : resolve()
            })
        })
    }
    catch(err)
    {
        ...handle errors...
    }
}

connect()

正如你所看到的,await 关键字会知道如何处理 Promise。你可以创建一个 Promise 并在回调实现中使用 resolve/reject 函数。其实就是这样,所以除非你经常访问数据库,否则使用包装器可能有点多余。


3
使用mysql-async-simple。

https://www.npmjs.com/package/mysql-async-simple

const { makeDb } = require('mysql-async-simple');
const mysql = require("mysql");
 
const connection = mysql.createConnection({
    host: process.env.HOST,
    user: process.env.USER,
    password: process.env.PASSWORD,
    database: process.env.DB
});
const db = makeDb();
await db.connect(connection);
 
try {
    const users = await db.query(connection, 'SELECT * FROM users');
} catch (e) {
    // handle exception
} finally {
    await db.close(connection);
}


3
正如LeOn - Han Li所述,我进行了一些小修改,因为我需要使用这个结果。
var mysql = require('mysql');
const util = require('util');

const conn = mysql.createConnection({
  host     : '127.0.0.1',
  user     : 'user',
  password : 'password',
  database : 'database'
});

const query = util.promisify(conn.query).bind(conn);

let result = async function() {
    var userCourse = [];
    try {
        const rows = await query('select * as count from file_managed');
    } finally {
        conn.end();
        return userCourse;
    }
};

result()
.then(value => {
    console.log(value)
});

2
您可以这样使用promise-mysql包:
const mysql = require('promise-mysql')

const getDbConnection = async () => {
  return await mysql.createConnection({
    host: process.env.HOST,
    user: process.env.USER,
    password: process.env.PASSWORD,
    database: process.env.DB
  })
}

const getUsers = async () => {
  const db = await getDbConnection()
  const users = await db.query("SELECT * FROM users")
  await db.end()
  return users
}


1

const { makeDb } = require('mysql-async-simple');
const mysql = require("mysql");
 
const connection = mysql.createConnection({
    host: process.env.HOST,
    user: process.env.USER,
    password: process.env.PASSWORD,
    database: process.env.DB
});
const db = makeDb();
await db.connect(connection);
 
try {
    const users = await db.query(connection, 'SELECT * FROM users');
} catch (e) {
    // handle exception
} finally {
    await db.close(connection);
}


1
你需要确保你正在使用的mysql库支持Promises,这是async/await所需的,或者使用类似Bluebird的promisifyAll的工具来包装该库。
async function appendedText() {
  const connection = await DatabasePool.getConnectionAsync();
  const [string1, string2, string3, string4] = await [
    connection.query(query1),
    connection.query(query2),
    connection.query(query3),
    connection.query(query4),
  ];
  return string1 + string2 + string3 + string4;
}

请注意,调用appendedText()实际上会返回一个Promise而不是一个值。
appendedText().then(appended_text => {});

2
你真的可以像那样使用await来模仿Promise.all吗?你知道在哪里可以找到更多信息吗?因为我的快速搜索返回了非常复杂的方法。 - adam-beck
@adam-beck,之前它是可以工作的...不过现在需要使用Promise.all()。请参见这里: https://dev59.com/iFsW5IYBdhLWcg3w-rPB - Alexis Wilke

0

我们可以在mysql.connect内部实现promise,而不是使用utilpromise/mysql

var con = require('mysql');

var mysql = con.createConnection({
    host: "localhost",
    user: "root",
    password: "pass",
    database: "test"
});

async function asyncAwait(req, res) {
    var promise1;
    mysql.connect((err) => {
        promise1 = new Promise((resolve, reject) => {
            console.log('Mysql: Connected');
            resolve(response.write(uc.upperCase('Connected\n')));
        });
        promise1
            .then(() => {
             //Implement the logic here
            })
            .catch(error => {
                console.log(error)
            });
    })
}
await asyncAwait();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接