このような単純化された機能があります。
function(query) {
myApi.exec('SomeCommand', function(response) {
return response;
});
}
基本的に私はそれがmyApi.exec
を呼び出して、そしてコールバックラムダで与えられる応答を返すことを望みます。ただし、上記のコードは機能せず、ただちに戻ります。
非常に厄介な試みのためだけに、私はうまくいかなかった以下を試みました、しかし、少なくともあなたは私が達成しようとしているものという考えを得ます:
function(query) {
var r;
myApi.exec('SomeCommand', function(response) {
r = response;
});
while (!r) {}
return r;
}
基本的に、これを実現するための良い 'node.js /イベント駆動'の方法は何ですか?私は自分の関数にコールバックが呼ばれるまで待ってからそれに渡された値を返すようにしたいです。
これを行うための「良いnode.js /イベント駆動型」の方法は、待たないことです。
Nodeのようなイベント駆動型システムを扱うときの他のほとんどすべてのものと同様に、関数は計算が完了したときに呼び出されるコールバックパラメータを受け取るべきです。呼び出し側は、通常の意味で値が「返される」のを待つのではなく、結果の値を処理するルーチンを送信します。
function(query, callback) {
myApi.exec('SomeCommand', function(response) {
// other stuff here...
// bla bla..
callback(response); // this will "return" your value to the original caller
});
}
だからあなたはこのようにそれを使用しないでください:
var returnValue = myFunction(query);
しかし、このように:
myFunction(query, function(returnValue) {
// use the return value here instead of like a regular (non-evented) return value
});
これを確認してください。 https://github.com/luciotato/waitfor-ES6
wait.for:でコードを生成します(ジェネレータが必要、 - ハーモニーフラグ)。
function* (query) {
var r = yield wait.for( myApi.exec, 'SomeCommand');
return r;
}
これを実現する1つの方法は、API呼び出しをpromiseにラップしてからawait
を使用して結果を待つことです。
// let's say this is the API function with two callbacks,
// one for success and the other for error
function apiFunction(query, successCallback, errorCallback) {
if (query == "bad query") {
errorCallback("problem with the query");
}
successCallback("Your query was <" + query + ">");
}
// myFunction wraps the above API call into a Promise
// and handles the callbacks with resolve and reject
function apiFunctionWrapper(query) {
return new Promise((resolve, reject) => {
apiFunction(query,(successResponse) => {
resolve(successResponse);
}, (errorResponse) => {
reject(errorResponse)
});
});
}
// now you can use await to get the result from the wrapped api function
// and you can use standard try-catch to handle the errors
async function businessLogic() {
try {
const result = await apiFunctionWrapper("query all users");
console.log(result);
// the next line will fail
const result2 = await apiFunctionWrapper("bad query");
} catch(error) {
console.error("ERROR:" + error);
}
}
// call the main function
businessLogic();
出力:
Your query was <query all users>
ERROR:problem with the query
あなたがコールバックを使いたくないならば、あなたは "Q"モジュールを使うことができます。
例えば:
function getdb() {
var deferred = Q.defer();
MongoClient.connect(databaseUrl, function(err, db) {
if (err) {
console.log("Problem connecting database");
deferred.reject(new Error(err));
} else {
var collection = db.collection("url");
deferred.resolve(collection);
}
});
return deferred.promise;
}
getdb().then(function(collection) {
// This function will be called afte getdb() will be executed.
}).fail(function(err){
// If Error accrued.
});
詳細についてはこれを参照してください。 https://github.com/kriskowal/q
他のコードを実行する前に、ノードでコールバック関数が実行されるのを待つのが非常に単純で簡単な、派手なライブラリではない場合、このようになります。
//initialize a global var to control the callback state
var callbackCount = 0;
//call the function that has a callback
someObj.executeCallback(function () {
callbackCount++;
runOtherCode();
});
someObj2.executeCallback(function () {
callbackCount++;
runOtherCode();
});
//call function that has to wait
continueExec();
function continueExec() {
//here is the trick, wait until var callbackCount is set number of callback functions
if (callbackCount < 2) {
setTimeout(continueExec, 1000);
return;
}
//Finally, do what you need
doSomeThing();
}
注:この回答は、プロダクションコードではおそらく使用しないでください。それはハックであり、あなたはその影響について知っておくべきです
uvrun モジュールがあります(新しいNodejsバージョン はこちら ) libuvメインイベントループ(Nodejsメインループ)を1回実行することができます。
あなたのコードは次のようになります。
function(query) {
var r;
myApi.exec('SomeCommand', function(response) {
r = response;
});
var uvrun = require("uvrun");
while (!r)
uvrun.runOnce();
return r;
}
(代わりにuvrun.runNoWait()
を使うかもしれません。それはブロッキングに関するいくつかの問題を避けることができますが、100%CPUを使います。)
このアプローチはNodejsの全体的な目的、つまりすべてを非同期にして非ブロック化することを無効にすることに注意してください。また、コールスタックの深さを大幅に増やすことができるため、スタックオーバーフローが発生する可能性があります。このような関数を再帰的に実行すると、間違いなく問題に遭遇します。
「正しい」ようにコードを再設計する方法に関する他の回答を参照してください。
ここでのこの解決策はおそらくテストやespをするときにだけ役に立つでしょう。同期とシリアルコードが欲しいのですが。
ノード4.8.0以降、ジェネレータと呼ばれるES6の機能を使用できます。より深い概念については、この 記事 に従ってください。しかし基本的にあなたはこの仕事を成し遂げるためにジェネレータと約束を使うことができます。私はジェネレータを約束し管理するために bluebird を使っています。
あなたのコードは以下の例のように問題ないはずです。
const Promise = require('bluebird');
function* getResponse(query) {
const r = yield new Promise(resolve => myApi.exec('SomeCommand', resolve);
return r;
}
Promise.coroutine(getResponse)()
.then(response => console.log(response));
私にはそれが使用するために働いた
JSON.parse(result)['key']
私は期待した結果に。これは「超一般的」ではないかもしれませんが、結局「JSON.parse」は非同期呼び出しの待機を管理しましたが、result['key']
はそうしませんでした。
関数があるとします。
var fetchPage(page, callback) {
....
request(uri, function (error, response, body) {
....
if (something_good) {
callback(true, page+1);
} else {
callback(false);
}
.....
});
};
あなたはこのようなコールバックを利用することができます:
fetchPage(1, x = function(next, page) {
if (next) {
console.log("^^^ CALLBACK --> fetchPage: " + page);
fetchPage(page, x);
}
});
exports.dbtest = function (req, res) {
db.query('SELECT * FROM users', [], res, renderDbtest);
};
function renderDbtest(result, res){
res.render('db', { result: result });
}
これが私がしたやり方です、あなたはそれと共に "res"を渡す必要があるので、後でレンダリングすることができます
これは、非ブロックIOの目的を無効にします。ブロックする必要がないときにブロックしている:)
Node.jsに待機させる代わりにコールバックをネストするか、r
の結果が必要なコールバック内で別のコールバックを呼び出す必要があります。
ブロックを強制する必要がある場合は、アーキテクチャが間違っていると考えている可能性があります。