web-dev-qa-db-ja.com

promiseを使用して、node.jsでMySQLの戻り値を処理します

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の非同期性のためにプロミスを返す必要があります。 pythonのようなnode.jsコードを書くことはできません。 getLastRecord()を変換してプロミスを返すにはどうすればよいですか?また、返された値をどのように処理しますか?

実際、私がやりたいのはこのようなものです。

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

Node.jsで読み取り可能な方法でこれを行うにはどうすればよいですか?

この場合、bluebirdを使用してpromiseを実装する方法を確認したいと思います。

24
user781486

これは少しバラバラになるでしょう、許してください。

まず、このコードがmysqlドライバーAPIを正しく使用していると仮定すると、ネイティブPromiseで動作するようにラップできる1つの方法があります。

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)の関数引数、nodeで見られる(err, result)関数、promise結果およびエラーハンドラーはすべてコールバックです。これは、特定の種類のコールバックを「コールバック」と呼ぶ人々によってやや混同されます。これは、「継続渡しスタイル」と呼ばれるものでノードコアで使用される(err, result)のコールバックです。 。

今のところ、少なくとも(最終的にはasync/awaitが来る)、promiseを採用するかどうかに関係なく、あなたはコールバックにかなりこだわっています。

また、コールバックがあるため、約束はすぐにではなく、ここでは明らかに役立つわけではありません。約束は、それらをPromise.allと組み合わせて、アキュムレータをArray.prototype.reduceと約束した場合にのみ本当に輝きます。しかし、彼らはdoときどき輝き、そしてare学ぶ価値があります。

44
Joshua Holbrook

Q(NPMモジュール)プロミスを使用するようにコードを変更しました。上記のスニペットで指定した「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);

 });
7
Piyush Sagar

私はNode.jsを初めて使用し、約束します。私は自分のニーズを満たすものをしばらく探していましたが、これは私が見つけたいくつかの例を組み合わせて使用​​したものです。クエリごとに接続を取得して、クエリの終了直後に接続を解放する機能(querySql)、またはプールから接続を取得してPromise.usingスコープ内で使用する、または必要に応じていつでも解放する機能が必要でした(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
};

sage_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;
7
MikeL

最初の質問に答えるには:node.jsで読みやすい方法でこれを行うにはどうすればよいですか?

co と呼ばれるライブラリがあります。これにより、同期ワークフローで非同期コードを記述することができます。ただ見て、npm install co

このアプローチで頻繁に直面する問題は、使用したいすべてのライブラリからPromiseを取得できないことです。したがって、自分でラップするか(@Joshua Holbrookの回答を参照)、ラッパーを探します(例:npm install mysql-promise

(ところで:ES7のロードマップでは、キーワードasyncawaitを使用してこのタイプのワークフローをネイティブにサポートしていますが、まだノードにはありません: ノード機能リスト =。)

3
CFrei

Promiseを使用する必要はありません。次のようなコールバック関数を使用できます。

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

   }
});
3
Jordi Ruiz

あなたが尋ねたように、これは非常に簡単に、例えばブルーバードで達成できます:

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)});
2
Andrej Burcev

私はまだノードに少し慣れていないので、何かを逃したかもしれません。非同期ノードをトリガーするのではなく、それを強制するだけなので、先に考えて計画する必要があります。

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に追加して、事前に約束を処理する新しい関数を作成しました。 (多くのノードの例では、これを「接続」と見なしています。

次に、promiseを使用してmysqlクエリを呼び出します。

      db.promise("SELECT * FROM users WHERE username='john doe' LIMIT 1;")
      .then((result)=>{
          console.log(result);
      }).catch((err)=>{
          console.log(err);
      });

これが役立つのは、最初のクエリに基づいて2番目のクエリを実行する必要がある場合です。

      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モジュールでpromiseを使用する例が得られるはずです。

また上記では、これらの約束の中でいつでも通常の方法でdb.queryを使用し続けることができ、それらは通常のように機能します。

これが死の三角形に役立つことを願っています。

1
Dillon Burnett

パッケージpromise-mysqlを使用すると、ロジックはthen(function(response){your code})を使用してpromiseをチェーンすることになります。

そして

catch(function(response){your code})を使用して、catchブロックの前の「then」ブロックからエラーをキャッチします。

このロジックに従って、ブロックの最後でreturnを使用して、オブジェクトまたは配列でクエリ結果を渡します。戻り値は、クエリ結果を次のブロックに渡すのに役立ちます。次に、結果は関数の引数にあります(ここではtest1)。このロジックを使用すると、複数のMySqlクエリと、結果を操作して必要な処理を実行するために必要なコードを連鎖させることができます。

すべてのブロックで作成されるすべてのオブジェクトと変数はローカルのみであるため、Connectionオブジェクトはグローバルに作成されます。より多くの "then"ブロックをチェーンできることを忘れないでください。

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