Nodejs中MySQL是否能够等待全部query回传结果才继续执行?

Nodejs中MySQL是否能够等待全部query回传结果才继续执行?

各位午安,方才我在进行 node 上头的 MySQL query,发现了一些问题,请先见程式码如下。

var getAllCouponsInfoByManagerUidAndRange = function (uid, range, callback) {
	// Select the special range in coupons, the range object have 'start' and 'amount'.
	var sql_1 = "SELECT * FROM coupon WHERE coupon_id BETWEEN " + range.start + " AND " + (range.start + range.amount - 1);
	dbclient.query(sql_1, function (err, coupons) {
		// Fixed the properties.
		coupons = TaskDateConvertToJson(coupons, new Array("create_time", "used_time"));

		for (var i = 0 ; i < coupons.length ; i++) {
			(function (j) {
				// Get the employees' user info of having received coupons.
				findUserByUid(coupons[j].uid, function (employee) {
					// Get the managers of creating coupons.
					findUserByUid(coupons[j].manager, function (manager) {
						// Get the employees' task info of having received coupons.
						findTaskByTid(coupons[j].tid, function (task) {
							coupons[j]['employee'] = employee;
							coupons[j]['manager'] = manager;
							coupons[j]['task'] = task;
							delete coupons[j]['uid'];
							delete coupons[j]['tid'];
							if (j == coupons.length - 1) 
								callback(coupons);
							console.log(coupons[j]);
						});
					});
				});
			})(i);
		}
	});
}

其中有一行

console.log(coupons[j]); 

是用来检查 query 的执行完毕顺序,

假使目前情况是 15 笔到 30 笔,因为部分条件不同,console.log 的结果会发现

15, 16, 18, 19, … , 30, 17。

第 17 笔经过较多层的 query 所以回传的时间较久,因为这样:

if (j == coupons.length - 1) 
  callback(coupons);

在完成第 30 笔时就执行 callback …其中回传的第 17 笔是没有 query 的结果。

还麻烦各位前辈协助解答,感谢!


7 回复

在Node.js中处理异步操作时,特别是涉及到数据库查询时,经常会遇到类似的问题。为了确保所有的查询都完成后再执行下一步操作,我们可以使用Promise或者async/await来更好地管理这些异步操作。

下面是改进后的代码示例,使用async/await来确保所有的查询都完成后再调用回调函数:

const mysql = require('mysql');
const dbclient = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'database_name'
});

const findUserByUid = async (uid) => {
    return new Promise((resolve, reject) => {
        dbclient.query("SELECT * FROM users WHERE uid = ?", [uid], (err, results) => {
            if (err) reject(err);
            resolve(results[0]);
        });
    });
};

const findTaskByTid = async (tid) => {
    return new Promise((resolve, reject) => {
        dbclient.query("SELECT * FROM tasks WHERE tid = ?", [tid], (err, results) => {
            if (err) reject(err);
            resolve(results[0]);
        });
    });
};

const getAllCouponsInfoByManagerUidAndRange = async (uid, range) => {
    try {
        // Select the special range in coupons, the range object have 'start' and 'end'.
        const sql_1 = "SELECT * FROM coupon WHERE coupon_id BETWEEN ? AND ?";
        const [coupons] = await dbclient.query(sql_1, [range.start, range.start + range.amount - 1]);

        // Convert the date fields to JSON format
        coupons = TaskDateConvertToJson(coupons, ["create_time", "used_time"]);

        // Process each coupon
        for (let j = 0; j < coupons.length; j++) {
            const [employee] = await findUserByUid(coupons[j].uid);
            const [manager] = await findUserByUid(coupons[j].manager);
            const [task] = await findTaskByTid(coupons[j].tid);

            coupons[j]['employee'] = employee;
            coupons[j]['manager'] = manager;
            coupons[j]['task'] = task;

            delete coupons[j]['uid'];
            delete coupons[j]['tid'];

            console.log(coupons[j]);
        }

        return coupons;
    } catch (error) {
        console.error(error);
    }
};

getAllCouponsInfoByManagerUidAndRange(uid, range)
    .then(coupons => {
        console.log('All queries completed:', coupons);
    })
    .catch(error => {
        console.error('Error occurred:', error);
    });

function TaskDateConvertToJson(data, fields) {
    data.forEach(item => {
        fields.forEach(field => {
            item[field] = JSON.stringify(item[field]);
        });
    });
    return data;
}

解释

  1. 使用async/await:通过将查询封装成返回Promise的函数(如findUserByUidfindTaskByTid),我们可以在主逻辑中使用await关键字来等待这些查询完成。
  2. 循环处理:在循环中,每个查询都是异步的,但通过await关键字,我们可以确保在处理下一个查询之前,当前查询已经完成。
  3. 错误处理:使用try/catch结构来捕获和处理可能发生的错误。

这种方法确保了所有的查询都按顺序完成,并且只有在所有查询都成功完成后才会调用回调函数。


用下 eventproxy

光看标题我觉得eventproxy可以解决这种问题。eventproxy也是异步的 又不是同步

我仔细看了下代码 这是典型的异步回调场景啊。 楼主无非是想 全部query执行完毕再回调,所以自己写了个函数,每次检查各个query的执行状态。 解决方法没问题,不过还是推荐直接eventproxy一行代码就搞定了。 就不用每次自己写函数了。

顺便,一般在不会遍历所有键的情况下(也就是说不会出特别严谨错误的使用姿势的时候),建议用 = undefined 代替 delete

不过这个是我自己的看法而已,只是推荐这样用。

http://www.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/

用howdo帮你重写了一遍

var howdo = require('howdo');
var getAllCouponsInfoByManagerUidAndRange2 = function(uid, range, callback) {
    // Select the special range in coupons, the range object have 'start' and 'amount'.
    var sql_1 = 'SELECT * FROM coupon WHERE coupon_id BETWEEN ' + range.start + ' AND ' + (range.start + range.amount - 1);
dbclient.query(sql_1, function(err, coupons) {
    if (err) {
        return callback(err);
    }

    // Fixed the properties.
    var coupons = TaskDateConvertToJson(coupons, ['create_time', 'used_time']);

    howdo.each(coupons, function(index, coupon, done) {

        howdo
        // Get the employees' user info of having received coupons.
        .task(function(done) {
            findUserByUid(coupon.uid, function(employee) {
                done(null, employee);
            });
        })
        // Get the managers of creating coupons.
        .task(function(done) {
            findUserByUid(coupon.manager, function(manager) {
                done(null, manager);
            });
        })
        // Get the employees' task info of having received coupons.
        .task(function(done) {
            findTaskByTid(coupon.tid, function(task) {
                done(null, task);
            });
        })
        // 并行无依赖结果
        .together(function(err, employee, manager, task) {
            coupon.employee = employee;
            coupon.manager = manager;
            coupon.task = task;

            delete coupon.uid;
            delete coupon.tid;

            done(null, coupon);
        });

    }).together(function(err, create_time, used_time) {
        // 做你该做的
        // ....
		callback(err, create_time, used_time);
    });
});

};

根据你的描述,问题在于当前代码没有等待所有嵌套的查询(findUserByUid、findTaskByTid等)都完成之后再执行回调函数。Node.js 中的异步操作需要使用一些控制流库或者 Promise 来确保所有查询完成后再执行后续逻辑。

你可以使用 Promiseasync/await 来简化代码并确保所有查询都完成后才执行回调。以下是一个修改后的示例代码:

const mysql = require('mysql');
const dbclient = mysql.createConnection({
  // 连接配置
});

function findUserByUid(uid) {
  return new Promise((resolve, reject) => {
    dbclient.query(`SELECT * FROM user WHERE uid=${uid}`, (err, rows) => {
      if (err) reject(err);
      resolve(rows[0]);
    });
  });
}

function findTaskByTid(tid) {
  return new Promise((resolve, reject) => {
    dbclient.query(`SELECT * FROM task WHERE tid=${tid}`, (err, rows) => {
      if (err) reject(err);
      resolve(rows[0]);
    });
  });
}

var getAllCouponsInfoByManagerUidAndRange = async function (uid, range) {
  try {
    const sql_1 = `SELECT * FROM coupon WHERE coupon_id BETWEEN ${range.start} AND ${range.start + range.amount - 1}`;
    const coupons = await new Promise((resolve, reject) => {
      dbclient.query(sql_1, (err, rows) => {
        if (err) reject(err);
        resolve(rows);
      });
    });

    for (let j = 0; j < coupons.length; j++) {
      coupons[j]['employee'] = await findUserByUid(coupons[j].uid);
      coupons[j]['manager'] = await findUserByUid(coupons[j].manager);
      coupons[j]['task'] = await findTaskByTid(coupons[j].tid);
      delete coupons[j]['uid'];
      delete coupons[j]['tid'];
    }

    return coupons;
  } catch (error) {
    console.error(error);
    throw error;
  }
};

getAllCouponsInfoByManagerUidAndRange(uid, range)
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error(error);
  });

在这个示例中,我们使用了 Promiseasync/await 来确保所有查询都完成后再返回最终的结果。这样可以避免由于异步操作导致的顺序混乱问题。

回到顶部