2016-04-11 3 views
14

У меня есть фон python и в настоящее время выполняется переход на node.js. У меня проблема с настройкой node.js из-за его асинхронного характера.Используйте обещание обработать возвращаемое значение MySQL в 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 читаемым способом?

Я хотел бы видеть, как обещания могут быть реализованы в этом случае с использованием синей птицы.

ответ

20

Это собирается быть немного рассеянно, простите меня.

Во-первых, если предположить, этот код использует MySQL API драйвера правильно, вот один из способов вы можете обернуть его на работу с родной обещание:

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), видимые в узле, и результат обетования и обработчики ошибок - это все обратные вызовы. Это несколько смущает людей, обращаясь к определенному виду обратного вызова, как «обратные вызовы», те из (err, result), которые используются в ядре узла в так называемом стиле продолжения-прохода, иногда называемом «nodebacks» людьми, которые им не очень нравятся ,

На данный момент, по крайней мере (async/await придет в конце концов), вы в значительной степени застряли с обратными вызовами, независимо от того, принимаете вы обещания или нет.

Кроме того, я хочу отметить, что обещания не сразу, очевидно, полезны здесь, поскольку у вас все еще есть обратный вызов. Обещания действительно действительно сияют, когда вы объединяете их с Promise.all и обещаете аккумуляторы a la Array.prototype.reduce. Но они сделать блеск иногда, и они стоит учиться.

+0

О, и если вы используете обещания, рассмотрите bluebird! ряд хороших помощников, хорошо понятый пер ormance и т. д. –

+0

Если я использую bluebird, могу ли я использовать функцию getLastRecord() 'и делать что-то вроде' Promisify (getLastRecord) 'и' getLastRecord() 'поддерживает обетование? – user781486

+0

Я думаю, что http://bluebirdjs.com/docs/api/promise.fromcallback.html - это то, что вы хотите –

3

Вам не нужно использовать обещание, вы можете использовать функцию обратного вызова, что-то вроде этого:

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 

    } 
}); 
+0

Спасибо. Есть ли способ сделать что-то вроде этого 'if (getLastRecord()> 20>' или хотя бы сделать его читаемым? – user781486

+1

@ user16891328 Вы должны сделать это внутри обратного вызова 'getLastRecord ('name_record', function (err, data) {if (err) {} else {if (data.length> 20)}}); ' –

+0

Ok. Спасибо. Кажется, что другого варианта нет. Код менее читабельен, чем python. – user781486

5

Я поменял ваш код на использование модуля Q (NPM). Я предположил, что ваша функция getLastRecord(), которую вы указали в приведенном выше фрагменте, работает правильно.

Вы можете обратиться ссылку разжиться модуля Q

Click here : Q documentation

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 в читаемом образом?

Существует библиотека под названием co, которая дает вам возможность писать асинхронный код в синхронном рабочем процессе. Просто посмотрите и npm install co.

Проблема, с которой вы сталкиваетесь очень часто с таким подходом, заключается в том, что вы не получаете Promise обратно со всех библиотек, которые хотите использовать. Таким образом, вы можете либо обернуть его самостоятельно (см. Ответ от @Joshua Holbrook), либо найти обертку (например: npm install mysql-promise)

(Кстати, на дорожной карте для ES7 есть встроенная поддержка этого типа рабочего процесса с ключевые слова asyncawait, но пока его не в узле:. node feature list)

2

Это может быть достигнуто довольно просто, например, с Блюберд, как вы просили:

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)}); 
4

Я новичок в nodejs и promisses. Я искал какое-то время для чего-то, что удовлетворит мои потребности, и это то, что я в конечном итоге использовал, объединив несколько методов, которые я нашел. Я хотел иметь возможность получить соединение для каждого запроса и отпустить его сразу после завершения запроса (querySql) или получить соединение из пула и использовать его в Promise.using scope или выпустить его, когда захочу (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; 
+0

Это довольно модульное и многоразовое. – Milind