Используйте обещание обработать возвращаемое значение MySQL в node.js
У меня есть фон python и в настоящее время переносится на node.js. У меня проблема с настройкой node.js из-за ее асинхронного характера.
Например, я пытаюсь вернуть значение из функции MySQL.
function getLastRecord(name)
{
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
console.log(err);
logger.info(err);
}
else {
//console.log(rows);
return rows;
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
}
var rows = getLastRecord('name_record');
console.log(rows);
После некоторого чтения я понимаю, что приведенный выше код не может работать, и мне нужно вернуть обещание из-за асинхронного характера node.js. Я не могу написать node.js код, например python. Как преобразовать getLastRecord()
, чтобы вернуть обещание и как обработать возвращаемое значение?
Фактически, я хочу сделать что-то вроде этого:
if (getLastRecord() > 20)
{
console.log("action");
}
Как это можно сделать в node.js читаемым способом?
Я хотел бы видеть, как promises можно реализовать в этом случае с помощью bluebird.
Ответы
Ответ 1
Это будет немного рассеяно, простите меня.
Во-первых, предполагая, что этот код правильно использует API-интерфейс mysql, здесь один из способов заставить его работать с собственным обещанием:
function getLastRecord(name)
{
return new Promise(function(resolve, reject) {
// The Promise constructor should catch any errors thrown on
// this tick. Alternately, try/catch and reject(err) on catch.
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
connection.query(query_str, query_var, function (err, rows, fields) {
// Call reject on error states,
// call resolve with results
if (err) {
return reject(err);
}
resolve(rows);
});
});
}
getLastRecord('name_record').then(function(rows) {
// now you have your rows, you can see if there are <20 of them
}).catch((err) => setImmediate(() => { throw err; })); // Throw async to escape the promise chain
Итак, одно: у вас все еще есть обратные вызовы. Обратные вызовы - это просто функции, которые вы даете кому-то, чтобы позвонить в какой-то момент в будущем с аргументами по его выбору. Таким образом, аргументы функции в xs.map(fn)
, функции (err, result)
, которые видны в node, а результат результата и обработчики ошибок - это все обратные вызовы. Это несколько смущает людей, обращаясь к определенному виду обратного вызова как "обратные вызовы", те из (err, result)
, которые используются в ядре node в том, что называется "стиль продолжения-прохода", иногда называемый "nodebacks" людьми, t действительно нравится им.
На данный момент, по крайней мере (async/await приходит в конце концов), вы в значительной степени застреваете с обратными вызовами, независимо от того, принимаете ли вы promises или нет.
Кроме того, я хочу отметить, что promises не сразу, очевидно, полезны здесь, так как у вас все еще есть обратный вызов. promises только сияет, когда вы объединяете их с Promise.all
и обещаете аккумуляторы a la Array.prototype.reduce
. Но иногда они блестят, и им стоит учиться.
Ответ 2
Я изменил свой код, чтобы использовать Q (модуль NPM) promises.
Я предположил, что ваша функция getLastRecord(), которую вы указали в приведенном выше фрагменте, работает правильно.
Вы можете ссылаться на следующую ссылку, чтобы получить модуль Q
Нажмите здесь: Q-документация
var q = require('q');
function getLastRecord(name)
{
var deferred = q.defer(); // Use Q
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
deferred.reject(err);
}
else {
//console.log(rows);
deferred.resolve(rows);
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
return deferred.promise;
}
// Call the method like this
getLastRecord('name_record')
.then(function(rows){
// This function get called, when success
console.log(rows);
},function(error){
// This function get called, when error
console.log(error);
});
Ответ 3
Я новичок в Node.js и обещания. Некоторое время я искал что-то, что соответствовало бы моим потребностям, и это то, чем я в конечном итоге воспользовался после объединения нескольких найденных примеров. Я хотел иметь возможность получать соединение для каждого запроса и освобождать его сразу после его завершения (querySql
), или получить соединение из пула и использовать его в рамках Promise.using, или освобождать его всякий раз, когда я этого getSqlConnection
(getSqlConnection
). Используя этот метод, вы можете объединять несколько запросов один за другим, не вкладывая их.
db.js
var mysql = require('mysql');
var Promise = require("bluebird");
Promise.promisifyAll(mysql);
Promise.promisifyAll(require("mysql/lib/Connection").prototype);
Promise.promisifyAll(require("mysql/lib/Pool").prototype);
var pool = mysql.createPool({
host: 'my_aws_host',
port: '3306',
user: 'my_user',
password: 'my_password',
database: 'db_name'
});
function getSqlConnection() {
return pool.getConnectionAsync().disposer(function (connection) {
console.log("Releasing connection back to pool")
connection.release();
});
}
function querySql (query, params) {
return Promise.using(getSqlConnection(), function (connection) {
console.log("Got connection from pool");
if (typeof params !== 'undefined'){
return connection.queryAsync(query, params);
} else {
return connection.queryAsync(query);
}
});
};
module.exports = {
getSqlConnection : getSqlConnection,
querySql : querySql
};
usage_route.js
var express = require('express');
var router = express.Router();
var dateFormat = require('dateformat');
var db = require('../my_modules/db');
var getSqlConnection = db.getSqlConnection;
var querySql = db.querySql;
var Promise = require("bluebird");
function retrieveUser(token) {
var userQuery = "select id, email from users where token = ?";
return querySql(userQuery, [token])
.then(function(rows){
if (rows.length == 0) {
return Promise.reject("did not find user");
}
var user = rows[0];
return user;
});
}
router.post('/', function (req, res, next) {
Promise.resolve().then(function () {
return retrieveUser(req.body.token);
})
.then(function (user){
email = user.email;
res.status(200).json({ "code": 0, "message": "success", "email": email});
})
.catch(function (err) {
console.error("got error: " + err);
if (err instanceof Error) {
res.status(400).send("General error");
} else {
res.status(200).json({ "code": 1000, "message": err });
}
});
});
module.exports = router;
Ответ 4
Вам не нужно использовать promises, вы можете использовать функцию обратного вызова, что-то вроде этого:
function getLastRecord(name, next)
{
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
console.log(err);
logger.info(err);
next(err);
}
else {
//console.log(rows);
next(null, rows);
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
}
getLastRecord('name_record', function(err, data) {
if(err) {
// handle the error
} else {
// handle your data
}
});
Ответ 5
Чтобы ответить на ваш первоначальный вопрос: как это можно сделать в node.js в удобочитаемой форме?
Существует библиотека с именем co
, которая дает вам возможность писать асинхронный код в синхронном рабочем процессе. Просто посмотрите и npm install co
.
Проблема, с которой вы часто сталкиваетесь при таком подходе, состоит в том, что вы не получаете Promise
от всех библиотек, которые вам нравятся. Таким образом, вы должны либо обернуть его самостоятельно (см. Ответ @Joshua Holbrook), либо искать оболочку (например: npm install mysql-promise
)
(Btw: его на дорожной карте ES7 иметь встроенную поддержку для этого типа процесса с ключевыми словами async
await
, но еще не в узле: узел список функций.)
Ответ 6
Это можно сделать довольно просто, например, с синей птицей, как вы просили:
var Promise = require('bluebird');
function getLastRecord(name)
{
return new Promise(function(resolve, reject){
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
console.log(err);
logger.info(err);
reject(err);
}
else {
resolve(rows);
//console.log(rows);
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
});
}
getLastRecord('name_record')
.then(function(rows){
if (rows > 20) {
console.log("action");
}
})
.error(function(e){console.log("Error handler " + e)})
.catch(function(e){console.log("Catch handler " + e)});
Ответ 7
Используя пакет promise-mysql, логика заключалась бы в цепочке обещаний с использованием then (function (response) {ваш код})
а также
catch (function (response) {ваш код}) для отлова ошибок из блоков "then", предшествующих блоку catch.
Следуя этой логике, вы передадите результаты запроса в объектах или массивах, используя return в конце блока. Возврат поможет передать результаты запроса в следующий блок. Затем результат будет найден в аргументе функции (здесь это test1). Используя эту логику, вы можете связать несколько запросов MySql и код, необходимый для манипулирования результатом и выполнения любых действий.
объект Connection создается глобальным, поскольку каждый объект и переменная, созданные в каждом блоке, являются только локальными. Не забывайте, что вы можете связать больше блоков "тогда".
var config = {
host : 'host',
user : 'user',
password : 'pass',
database : 'database',
};
var mysql = require('promise-mysql');
var connection;
let thename =""; // which can also be an argument if you embed this code in a function
mysql.createConnection(config
).then(function(conn){
connection = conn;
let test = connection.query('select name from records WHERE name=? LIMIT 1',[thename]);
return test;
}).then(function(test1){
console.log("test1"+JSON.stringify(test1)); // result of previous block
var result = connection.query('select * from users'); // A second query if you want
connection.end();
connection = {};
return result;
}).catch(function(error){
if (connection && connection.end) connection.end();
//logs out the error from the previous block (if there is any issue add a second catch behind this one)
console.log(error);
});
Ответ 8
Я все еще немного новичок в ноде, поэтому, может быть, я что-то пропустил, дайте мне знать, как это работает. Вместо того, чтобы запускать асинхронный узел, просто навязывайте его вам, поэтому вы должны подумать заранее и спланировать это.
const mysql = require('mysql');
const db = mysql.createConnection({
host: 'localhost',
user: 'user', password: 'password',
database: 'database',
});
db.connect((err) => {
// you should probably add reject instead of throwing error
// reject(new Error());
if(err){throw err;}
console.log('Mysql: Connected');
});
db.promise = (sql) => {
return new Promise((resolve, reject) => {
db.query(sql, (err, result) => {
if(err){reject(new Error());}
else{resolve(result);}
});
});
};
Здесь я использую модуль mysql как обычно, но вместо этого я создал новую функцию для обработки обещания заранее, добавив его в db const. (Вы видите это как "соединение" во многих примерах узлов.
Теперь давайте вызовем запрос MySQL, используя обещание.
db.promise("SELECT * FROM users WHERE username='john doe' LIMIT 1;")
.then((result)=>{
console.log(result);
}).catch((err)=>{
console.log(err);
});
Я нашел это полезным для вас, когда вам нужно сделать второй запрос на основе первого запроса.
db.promise("SELECT * FROM users WHERE username='john doe' LIMIT 1;")
.then((result)=>{
console.log(result);
var sql = "SELECT * FROM friends WHERE username='";
sql = result[0];
sql = "';"
return db.promise(sql);
}).then((result)=>{
console.log(result);
}).catch((err)=>{
console.log(err);
});
На самом деле вы должны использовать переменные mysql, но это должно по крайней мере дать вам пример использования обещаний с модулем mysql.
Также с вышеупомянутым вы все равно можете продолжать использовать db.query обычным способом в любое время в рамках этих обещаний, они просто работают как обычно.
Надеюсь, это поможет с треугольником смерти.