Node.js обработка ошибок mysql в обещании

Я использую 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

Правильный запрос, нет проблем ... Есть идеи, что я сделал не так с обработкой ошибок?

Вместо того, чтобы выдавать ошибку, вы должны отклонить обещание. Кроме того, ваш код выше смешивает одинарные и двойные кавычки в вашем запросе.

Mike Atkins 31.03.2018 13:33
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
0
1
1 698
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы повторно генерируете ошибку, переданную обратному вызову вашего запроса, которую библиотека, которую вы используете, также повторно выбрасывает, и, наконец, нигде должным образом не перехватывается и не обрабатывается и приводит к сбою. Когда вы используете 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 01.04.2018 02:35

@zebullon Извините, я пропустил добавление await, прежде чем вернуть его. В предыдущем решении была ошибка, из-за которой соединение возвращалось в пул до завершения запроса. Теперь, когда здесь присутствует выражение await, он сначала будет ждать завершения запроса перед выполнением блока finally (и возвратом значения). Имеет ли это смысл?

Sven 01.04.2018 03:22

Я также должен объяснить причину добавления предложений try/finally к этой функции. Если connection.query() выдает ошибку, выполнение на этом останавливается, и ошибка распространяется до вызываемого, то есть connection.release() никогда не будет запущен. Заключение двух вызовов в предложения try и finally гарантирует, что метод connection.release() будет вызван даже в случае ошибки. Однако, если ошибка sqlPool.getConnection(), у нас нет соединения для возврата к пулу, поэтому он помещает за пределами в предложение try.

Sven 01.04.2018 03:32

Если вы не уверены в потоке выполнения, я настроил для вас ответ здесь. Изучите и запустите три функции getData, getDataConnectionError и getDataQueryError, чтобы лучше понять.

Sven 01.04.2018 03:44

Другие вопросы по теме