2016-04-11 18 views
14

Ho uno sfondo pitone e sto attualmente migrando su node.js. Ho problemi con l'adattamento a node.js a causa della sua natura asincrona.Utilizzare la promessa per elaborare il valore di ritorno MySQL in node.js

Ad esempio, sto cercando di restituire un valore da una funzione 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); 

Dopo un po 'di lettura fino, mi rendo conto il codice di cui sopra non può lavorare e ho bisogno di tornare una promessa a causa della natura asincrona di node.js. Non riesco a scrivere codice node.js come python. Come faccio a convertire getLastRecord() per restituire una promessa e come gestisco il valore restituito?

In effetti, quello che voglio fare è qualcosa del genere;

if (getLastRecord() > 20) 
{ 
    console.log("action"); 
} 

Come può essere fatto in node.js in modo leggibile?

Mi piacerebbe vedere come le promesse possono essere implementate in questo caso usando bluebird.

risposta

20

Questo sta andando essere un po 'sparsi, perdonami.

In primo luogo, supponendo che questo codice utilizza il driver API mysql correttamente, ecco un modo si potrebbe avvolgere di lavorare con una promessa originaria:

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 

Quindi una cosa: Hai ancora callback. Le callback sono solo funzioni che si passano a qualcosa da chiamare ad un certo punto nel futuro con argomenti di sua scelta. Pertanto, gli argomenti della funzione in xs.map(fn), le funzioni (err, result) visualizzate nel nodo, il risultato della promessa e i gestori degli errori sono tutte richiamate. Questo è un po 'confuso dalle persone che si riferiscono a un tipo specifico di callback come "callback", quelle di (err, result) utilizzate nel nodo core in quello che viene chiamato "stile di passaggio continuo", a volte chiamato "nodi di nodo" da persone a cui non piacciono veramente .

Per ora, almeno (async/attendi arriverà prima o poi), sei praticamente bloccato dai callback, indipendentemente dal fatto che tu adotti o meno delle promesse.

Inoltre, noterò che le promesse non sono immediatamente, ovviamente utili qui, dato che hai ancora una richiamata. Le promesse brillano solo quando le combini con Promise.all e promuovi accumulatori a la Array.prototype.reduce. Ma a volte lo fanno il lucentezza, e sono che vale la pena imparare.

+0

Oh, e se usi le promesse, considera bluebird! un numero di simpatici aiutanti, perfetti ben compresi ormance, ecc. –

+0

Se uso bluebird, posso utilizzare la funzione 'getLastRecord()' e fare qualcosa come 'Promisify (getLastRecord)' e 'getLastRecord()' supporta la promessa? – user781486

+0

Penso che http://bluebirdjs.com/docs/api/promise.fromcallback.html sia quello che desideri –

3

Non è necessario utilizzare le promesse, è possibile utilizzare una funzione di callback, qualcosa di simile:

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

Grazie. C'è un modo per fare qualcosa di simile a 'if (getLastRecord()> 20>' o almeno renderlo leggibile? – user781486

+1

@ user16891328 Devi farlo all'interno del callback, 'getLastRecord ('name_record', function (err, data) {if (err) {} else {if (data.length> 20)}}); ' –

+0

Ok. Grazie, sembra che non ci siano altre opzioni.Il codice è meno leggibile di python. – user781486

5

Ho modificato il codice per utilizzare le promesse Q (modulo NPM). Ho presupposto che la funzione 'getLastRecord()' specificata nello snippet precedente funzioni correttamente.

È possibile fare riferimento seguente Link per entrare in possesso di modulo 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

Per rispondere alla tua domanda iniziale: come può questo essere fatto in node.js in modo leggibile?

C'è una libreria chiamata co, che ti dà la possibilità di scrivere codice asincrono in un flusso di lavoro sincrono. Basta dare un'occhiata e npm install co.

Il problema con cui ci si confronta molto spesso con questo approccio è che non si ottiene Promise indietro da tutte le librerie che si desidera utilizzare. Quindi puoi avvolgerlo tu stesso (vedi risposta da @Joshua Holbrook) o cercare un wrapper (ad esempio: npm install mysql-promise)

(Btw: è sulla roadmap per ES7 avere supporto nativo per questo tipo di flusso di lavoro con parole chiave asyncawait, ma la sua non ancora in nodo:. node feature list)

2

Ciò può essere ottenuto semplicemente, per esempio con Bluebird, come lei ha chiesto:

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

sono nuovo di nodejs e promisses. Stavo cercando un po 'di tempo per qualcosa che soddisfi i miei bisogni e questo è quello che ho finito usando dopo aver combinato diverse pratiche che ho trovato. Volevo la possibilità di acquisire la connessione per query e rilasciarlo subito dopo il termine della query (querySql) o per ottenere una connessione dal pool e utilizzarlo nell'ambito di Promise.using o rilasciarlo quando lo vorrei (getSqlConnection). Utilizzando questo metodo è possibile concatenare più query una dopo l'altra senza annidarle.

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

Questo è abbastanza modulare e riutilizzabile. – Milind