Я использую node.js и express, а также mysql.
Я использую пул соединений для запроса соединений и создания на нем обещания, чтобы ограничить кошмар обратного вызова, следующий фрагмент установлен в файле, который я импортирую позже, обратите внимание, что я установил обработчик на error, чтобы не завершать приложение в случае что-то действительно идет не так
exports.getConnection = () => {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) {
reject(`Could not obtain the connection from the pool: ${err}`);
}
connection.on('error', err => {
console.info(`SQL error (code: ${err.code}, message: ${err.sqlMessage}) while executing query: ${err.sql}`);
});
resolve(connection);
});
});
};
И вот пример использования (идея состоит в том, чтобы получить соединение, связать запрос в then, и если произойдет нефатальная ошибка, я брошу ее и обработаю освобождение соединения в обработчике catch.
// Exception handler that release the connection then call the callback
function releaseConnectionHandler(err, connection, callback) {
connection.release();
callback(err, null);
}
exports.someRequest = function(ID, callback) {
sqlPool.getConnection().then(connection => {
connection.query("SELECT * from tableNotExists",
(err, result) => {
if (err) {
throw ({ err, connection, callback });
}
connection.release();
callback(null, result);
});
}).catch(({ err, connection, callback}) => releaseConnectionHandler(err, connection, callback));
};
Запрос не будет выполнен, но я вижу, что обработчик даже не вызывается (я вставил в него какой-то след ...), и приложение завершается на
node_modules/mysql/lib/protocol/Parser.js:80
throw err; // Rethrow non-MySQL errors
Правильный запрос, нет проблем ... Есть идеи, что я сделал не так с обработкой ошибок?






Вы повторно генерируете ошибку, переданную обратному вызову вашего запроса, которую библиотека, которую вы используете, также повторно выбрасывает, и, наконец, нигде должным образом не перехватывается и не обрабатывается и приводит к сбою. Когда вы используете throw, вы находитесь не в контексте обещания, а в контексте функции обратного вызова, вызываемой из модуля mysql.
Вы также без надобности смешиваете обещания и обратные вызовы, в частности экспортируемую функцию. Ваш вопрос указывает на то, что вы хотите отказаться от обратных вызовов, поэтому я собираюсь основывать свой ответ на этом указании.
Чтобы решить основную проблему, не выдавайте ошибку. Вместо этого передайте его вызываемому:
const promisify = require("util").promisify;
exports.someRequest = function (ID) {
return sqlPool.getConnection().then(connection => {
return promisify(connection.query)("select * from tableNotExist")
.finally(connection.release);
});
};
Соединение всегда будет возвращено в пул, независимо от того, успешно оно или при ошибке. Затем вы можете вызвать метод с помощью:
yourModule.someRequest(id).then((results) => {
// Do something with the result set
}).catch((e) => {
// Handle error. Can be either a pool connection error or a query error.
});
Если у вас есть возможность использовать async/await, код можно переписать:
const promisify = require("util").promisify;
exports.someRequest = async function (ID) {
let connection = await sqlPool.getConnection();
try {
return await promisify(connection.query)("select * from tableNotExist");
} finally {
connection.release();
}
};
Я также рекомендую использовать node-mysql2, поскольку у них есть API на основе обещаний в дополнение к API в стиле обратного вызова, а также, по моему опыту, более высокая производительность. Тогда вам не нужно писать эти утомительные обертки, а вместо этого просто require('mysql2/promise') и готово.
В качестве дополнительного вопроса, почему обещание возврата все еще запоминает включающий блок try{...} finally {...}? Исходя из C++, это кажется очень странным, я бы не подумал, что это имеет какое-либо отношение к закрытию, поскольку AFAIK это касается включающих переменных, а не спецификаторов try-catch-finally?
@zebullon Извините, я пропустил добавление await, прежде чем вернуть его. В предыдущем решении была ошибка, из-за которой соединение возвращалось в пул до завершения запроса. Теперь, когда здесь присутствует выражение await, он сначала будет ждать завершения запроса перед выполнением блока finally (и возвратом значения). Имеет ли это смысл?
Я также должен объяснить причину добавления предложений try/finally к этой функции. Если connection.query() выдает ошибку, выполнение на этом останавливается, и ошибка распространяется до вызываемого, то есть connection.release() никогда не будет запущен. Заключение двух вызовов в предложения try и finally гарантирует, что метод connection.release() будет вызван даже в случае ошибки. Однако, если ошибка sqlPool.getConnection(), у нас нет соединения для возврата к пулу, поэтому он помещает за пределами в предложение try.
Если вы не уверены в потоке выполнения, я настроил для вас ответ здесь. Изучите и запустите три функции getData, getDataConnectionError и getDataQueryError, чтобы лучше понять.
Вместо того, чтобы выдавать ошибку, вы должны отклонить обещание. Кроме того, ваш код выше смешивает одинарные и двойные кавычки в вашем запросе.