如何使一个函数等待,直到使用node.js调用callback

我有一个简化的函数,看起来像这样:

function(query) { myApi.exec('SomeCommand', function(response) { return response; }); } 

基本上我希望它调用myApi.exec ,并返回callbacklambda中给出的响应。 但是,上面的代码不起作用,只是立即返回。

只是为了一个非常冒险的尝试,我试了下面哪个都没用,但是至less你明白了我想要达到的目的:

 function(query) { var r; myApi.exec('SomeCommand', function(response) { r = response; }); while (!r) {} return r; } 

基本上,这是一个很好的“node.js /事件驱动”方式吗? 我希望我的函数等待callback被调用,然后返回传递给它的值。

这样做的“好node.js /事件驱动”的方式是不要等待

像使用事件驱动的系统(比如节点)一样,几乎所有的事情都是一样的,你的函数应该接受一个callback参数,这个参数在计算完成后会被调用。 调用者不应该等待正常意义上的“返回”值,而是发送处理结果值的例程:

 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 :(需要生成器,–harmony标志)

 function* (query) { var r = yield wait.for( myApi.exec, 'SomeCommand'); return r; } 

如果您不想使用回拨,则可以使用“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

注意:这个答案可能不应该用在生产代码中。 这是一个黑客,你应该知道的影响。

有一个uvrun模块( 在这里更新为更新的Nodejs版本),你可以在libuv主事件循环(这是Nodejs主循环)中执行一个循环。

你的代码看起来像这样:

 function(query) { var r; myApi.exec('SomeCommand', function(response) { r = response; }); var uvrun = require("uvrun"); while (!r) uvrun.runOnce(); return r; } 

(你可以select使用uvrun.runNoWait() ,这样可以避免一些阻塞问题,但是占用100%CPU。)

请注意,这种方法会使Nodejs的整个目的无效,即具有asynchronous和非阻塞的一切。 此外,它可以增加你的调用堆栈深度,所以你可能会结束堆栈溢出。 如果你recursion运行这个函数,你肯定会遇到麻烦。

请参阅关于如何重新devise代码的其他答案,以便“正确”。

这里的解决scheme可能只在testing和esp时才有用。 想要同步和串行代码。

如果你想要它非常简单,没有花哨的库,等待callback函数在节点执行,在执行其他代码之前,是这样的:

 //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(); } 

这打破了非阻塞IO的目的 – 当你不需要阻塞的时候你会阻止它

你应该嵌套你的callback,而不是强迫node.js等待,或者在你需要r的结果的callback中调用另一个callback。

有可能,如果你需要强制阻止,你正在考虑你的架构错误。

由于节点4.8.0,您可以使用称为发生器的ES6function。 您可以按照这篇文章获得更深入的概念。 但基本上你可以使用生成器和承诺来完成这项工作。 我正在使用蓝鸟 promisify和pipe理生成器。

你的代码应该像下面的例子一样好。

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

假设你有一个function:

 var fetchPage(page, callback) { .... request(uri, function (error, response, body) { .... if (something_good) { callback(true, page+1); } else { callback(false); } ..... }); }; 

你可以使用像这样的callback:

 fetchPage(1, x = function(next, page) { if (next) { console.log("^^^ CALLBACK --> fetchPage: " + page); fetchPage(page, x); } }); 

对我来说,它使用

 JSON.parse(result)['key'] 

我期望的结果。 这可能不是“超级普通”,但最后“JSON.parse”pipe理等待asynchronous调用,而result['key']没有。

 exports.dbtest = function (req, res) { db.query('SELECT * FROM users', [], res, renderDbtest); }; function renderDbtest(result, res){ res.render('db', { result: result }); } 

这是我如何做到的,你只需要通过“res”就可以,所以你可以稍后渲染