私は、イベント/コールバックスタイルのプログラミングとNodeJSの両方の初心者です。 node-mysqlモジュールを使用してddbbデータを提供する小さなhttpサーバーを実装しようとしています。
私の問題は、クエリの構造化に起因しています。多くの場合、以前のクエリの結果を実行する必要があるクエリがあるため、それらすべてを同時に(非同期で)実行することはできず、結果を待機する必要があります。
私の最初のアプローチは、すべての非依存クエリを同時に実行し、それらがすべて完了したことを示すフラグを設定するまでループすることでしたので、依存(同期)クエリを続行できますが、これが正しいアプローチかどうかを知る。
このようなもの:
function x(){
var result_for_asynch_query_1 = null
var result_for_asynch_query_2 = null
mainLoop(){
// call non-dependant query 1
// call non-dependant query 2
// loop until vars are != null
// continue with queries that require data from the first ones
}
}
//for each browser request
httpServer{
call_to_x();
}.listen();
このようにして、すべての応答を連続して待機するのではなく、最長の応答のみを待機するため、最終結果の時間を節約できます。
これを行う一般的な方法はありますか?私がフォローしていないデザインパターンはありますか?
他の方法で考えてみてください(非同期フローの良い紹介があります howtonode.org )
var db = get_link_or_pool();
do_queries( callback ) {
db.query(sql1, function(err, res1) {
if (err) {
callback(err);
return;
}
// use res1 ...
db.query(sql2, function(err, res2) {
if (err) {
callback(err);
return;
}
callback(null, res2); // think 'return'
}
});
}
request_handler(req) {
do_queries( function(err, result) {
if(err)
report_error(err);
else
write_result(req, result);
});
}
運命のピラミッドを避けるべきです:
var express = require('express');
var Q = require('Q');
var app = express();
app.get('/',function(req,res){
var mysql = require('mysql');
var connection = mysql.createConnection({
Host : 'localhost',
user : 'root',
password : ''
});
connection.connect();
function doQuery1(){
var defered = Q.defer();
connection.query('SELECT 1 AS solution',defered.makeNodeResolver());
return defered.promise;
}
function doQuery2(){
var defered = Q.defer();
connection.query('SELECT 2 AS solution',defered.makeNodeResolver());
return defered.promise;
}
Q.all([doQuery1(),doQuery2()]).then(function(results){
res.send(JSON.stringify(results[0][0][0].solution+results[1][0][0].solution));
// Hint : your third query would go here
});
connection.end();
});
app.listen(80);
console.log('Listening on port 80');
このサンプルは、2つの独立した計算値に依存する結果を示しています。これらの各値は、doQuery1およびdoQuery2で照会されます。これらは順番に実行されますが、非同期に実行されます。
次に、Q.all(...
基本的に、成功時に「then」コールバックを呼び出します。そのコールバック内で、計算が行われます。
Promise(詳細: Github Q:Javascriptのpromise および wikipedia )を使用すると、コードをよりクリーンにし、計算と結果の処理を分離し、物事を移動できます。
計算の前提条件として「doQuery3」を追加するのがどれほど簡単かを見てください。
そして、サンプルコードに続く「package.json」を以下に示します。
{
"name": "hello-world",
"description": "hello world test app",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "3.2.0",
"q": "0.9.3",
"mysql":"2.0.0-alpha7"
}
}
別の解決策は、すべてのステートメントを連結し、各ステートメントをセミコロンで終了することです。たとえば、複数のテーブルから選択するには、次のクエリを使用できます。
_var sql = 'select * from user; select * from admin;'
_
次に、1つの接続のみを使用して複数のステートメントを実行できます。
var connection = mysql.createConnection({multipleStatements: true}) connection.query(sql)
注:SQLインジェクションを防ぐため、デフォルトでは複数のステートメントが無効になっています。すべての値を適切にエスケープしてください (ドキュメントを参照) 。
私はこの問題を克服するのに非常に役立つ以下を見つけました:
http://book.mixu.net/node/ch7.html から取得-他にも多くの素晴らしい例がここにあります!!
function async(arg, callback) {
console.log('do something with \''+arg+'\', return 1 sec later');
//replace setTimeout with database query
setTimeout(function() { callback(arg * 2); }, 1000);
}
// Final task (return data / perform further operations)
function final() { console.log('Done', results); }
// A simple async series:
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
function series(item) {
if(item) {
async( item, function(result) {
results.Push(result);
return series(items.shift());
});
} else {
return final();
}
}
series(items.shift());
「アイテムのセットを取得し、最初のアイテムでシリーズ制御フロー関数を呼び出します。シリーズは1つのasync()操作を起動し、それにコールバックを渡します。コールバックは結果を結果配列にプッシュし、次に次でシリーズを呼び出しますアイテム配列のアイテム。アイテム配列が空の場合、final()関数を呼び出します。」 ( http://book.mixu.net/node/ch7.html から)