如何使函数一直等待,直到使用node.js调用回调函数为止?

时间:2022-01-20 00:40:53

I have a simplified function that looks like this:

我有一个这样的简化函数:

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

Basically i want it to call myApi.exec, and return the response that is given in the callback lambda. However, the above code doesn't work and simply returns immediately.

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

Just for a very hackish attempt, i tried the below which didn't work, but at least you get the idea what i'm trying to achieve:

我尝试了下面的方法,但至少你明白了我想要达到的目的:

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

Basically, what's a good 'node.js/event driven' way of going about this? I want my function to wait until the callback gets called, then return the value that was passed to it.

基本上,什么是好的“节点”。js/事件驱动的方式?我希望我的函数等待回调函数调用,然后返回传递给它的值。

10 个解决方案

#1


231  

The "good node.js /event driven" way of doing this is to not wait.

“好节点。js /事件驱动的“这样做的方式是不等待。

Like almost everything else when working with event driven systems like node, your function should accept a callback parameter that will be invoked when then computation is complete. The caller should not wait for the value to be "returned" in the normal sense, but rather send the routine that will handle the resulting value:

与在与事件驱动的系统(如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
  });
}

So you dont use it like this:

所以你不用这样做:

var returnValue = myFunction(query);

But like this:

但像这样:

myFunction(query, function(returnValue) {
  // use the return value here instead of like a regular (non-evented) return value
});

#2


20  

check this: https://github.com/luciotato/waitfor-ES6

检查:https://github.com/luciotato/waitfor-ES6

your code with wait.for: (requires generators, --harmony flag)

您的代码与等待。for:(需要发电机,——和谐旗帜)

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

#3


10  

If you don't want to use call back then you can Use "Q" module.

如果你不想使用回电,那么你可以使用“Q”模块。

For example:

例如:

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. 

});

For more information refer this: https://github.com/kriskowal/q

更多信息请参考:https://github.com/kriskowal/q。

#4


5  

Note: This answer should probably not be used in production code. It's a hack and you should know about the implications.

注意:这个答案可能不应该用于生产代码。这是一个hack,你应该知道它的含义。

There is the uvrun module (updated for newer Nodejs versions here) where you can execute a single loop round of the libuv main event loop (which is the Nodejs main loop).

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

Your code would look like this:

代码是这样的:

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

(You might alternative use uvrun.runNoWait(). That could avoid some problems with blocking, but takes 100% CPU.)

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

Note that this approach kind of invalidates the whole purpose of Nodejs, i.e. to have everything async and non-blocking. Also, it could increase your callstack depth a lot, so you might end up with stack overflows. If you run such function recursively, you definitely will run into troubles.

请注意,这种方法会使node . js的整个目的变得无效,即拥有所有的异步和非阻塞。此外,它还可以增加您的调用堆栈深度,因此您可能最终会出现堆栈溢出。如果你递归地运行这样的函数,你肯定会遇到麻烦。

See the other answers about how to redesign your code to do it "right".

关于如何重新设计代码以实现“正确”,请参阅其他答案。

This solution here is probably only useful when you do testing and esp. want to have synced and serial code.

这个解决方案可能只在您进行测试和支持时才有用。

#5


5  

If you want it very simple and easy, no fancy libraries, to wait for callback functions to be executed in node, before executing some other code, is like this:

如果您希望它非常简单和简单,没有高级库,在执行其他代码之前,等待在节点中执行回调函数,就像这样:

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

#6


3  

Since node 4.8.0 you are able to use the feature of ES6 called generator. You may follow this article for deeper concepts. But basically you can use generators and promises to get this job done. I'm using bluebird to promisify and manage the generator.

由于节点4.8.0,您可以使用ES6的特性称为生成器。你可以跟随这篇文章了解更深层次的概念。但基本上你可以使用发电机,并承诺完成这项工作。我用蓝鸟来宣传和管理发电机。

Your code should be fine like the example below.

您的代码应该很好,就像下面的示例一样。

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

#7


2  

That defeats the purpose of non-blocking IO -- you're blocking it when it doesn't need blocking :)

这违背了非阻塞IO的目的——当它不需要阻塞时,您就阻塞了它:)

You should nest your callbacks instead of forcing node.js to wait, or call another callback inside the callback where you need the result of r.

您应该将回调设置为嵌套,而不是强制节点。要等待,或在回调中调用另一个回调,在这个回调中,您需要r的结果。

Chances are, if you need to force blocking, you're thinking about your architecture wrong.

很有可能,如果您需要强制阻塞,那么您正在考虑您的体系结构错误。

#8


1  

supposing you have a function:

假设你有一个函数:

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


};

you can make use of callbacks like this:

你可以使用这样的回调:

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

#9


1  

To me it worked to use

对我来说,它是有用的。

JSON.parse(result)['key']

on the result I expected. This may not be "super general", but in the end "JSON.parse" managed the waiting for the asyncronous call, whilst result['key'] didn't.

就我所期望的结果。这可能不是“超级通用”,但最终是“JSON”。解析“管理等待异步调用的过程,而结果['key']则没有。

#10


0  

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

function renderDbtest(result, res){
  res.render('db', { result: result });
}

Here is how I did it, you just have to pass "res" with it, so you can render later

我是这样做的,你只需要通过“res”,你就可以在后面渲染了。

#1


231  

The "good node.js /event driven" way of doing this is to not wait.

“好节点。js /事件驱动的“这样做的方式是不等待。

Like almost everything else when working with event driven systems like node, your function should accept a callback parameter that will be invoked when then computation is complete. The caller should not wait for the value to be "returned" in the normal sense, but rather send the routine that will handle the resulting value:

与在与事件驱动的系统(如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
  });
}

So you dont use it like this:

所以你不用这样做:

var returnValue = myFunction(query);

But like this:

但像这样:

myFunction(query, function(returnValue) {
  // use the return value here instead of like a regular (non-evented) return value
});

#2


20  

check this: https://github.com/luciotato/waitfor-ES6

检查:https://github.com/luciotato/waitfor-ES6

your code with wait.for: (requires generators, --harmony flag)

您的代码与等待。for:(需要发电机,——和谐旗帜)

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

#3


10  

If you don't want to use call back then you can Use "Q" module.

如果你不想使用回电,那么你可以使用“Q”模块。

For example:

例如:

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. 

});

For more information refer this: https://github.com/kriskowal/q

更多信息请参考:https://github.com/kriskowal/q。

#4


5  

Note: This answer should probably not be used in production code. It's a hack and you should know about the implications.

注意:这个答案可能不应该用于生产代码。这是一个hack,你应该知道它的含义。

There is the uvrun module (updated for newer Nodejs versions here) where you can execute a single loop round of the libuv main event loop (which is the Nodejs main loop).

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

Your code would look like this:

代码是这样的:

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

(You might alternative use uvrun.runNoWait(). That could avoid some problems with blocking, but takes 100% CPU.)

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

Note that this approach kind of invalidates the whole purpose of Nodejs, i.e. to have everything async and non-blocking. Also, it could increase your callstack depth a lot, so you might end up with stack overflows. If you run such function recursively, you definitely will run into troubles.

请注意,这种方法会使node . js的整个目的变得无效,即拥有所有的异步和非阻塞。此外,它还可以增加您的调用堆栈深度,因此您可能最终会出现堆栈溢出。如果你递归地运行这样的函数,你肯定会遇到麻烦。

See the other answers about how to redesign your code to do it "right".

关于如何重新设计代码以实现“正确”,请参阅其他答案。

This solution here is probably only useful when you do testing and esp. want to have synced and serial code.

这个解决方案可能只在您进行测试和支持时才有用。

#5


5  

If you want it very simple and easy, no fancy libraries, to wait for callback functions to be executed in node, before executing some other code, is like this:

如果您希望它非常简单和简单,没有高级库,在执行其他代码之前,等待在节点中执行回调函数,就像这样:

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

#6


3  

Since node 4.8.0 you are able to use the feature of ES6 called generator. You may follow this article for deeper concepts. But basically you can use generators and promises to get this job done. I'm using bluebird to promisify and manage the generator.

由于节点4.8.0,您可以使用ES6的特性称为生成器。你可以跟随这篇文章了解更深层次的概念。但基本上你可以使用发电机,并承诺完成这项工作。我用蓝鸟来宣传和管理发电机。

Your code should be fine like the example below.

您的代码应该很好,就像下面的示例一样。

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

#7


2  

That defeats the purpose of non-blocking IO -- you're blocking it when it doesn't need blocking :)

这违背了非阻塞IO的目的——当它不需要阻塞时,您就阻塞了它:)

You should nest your callbacks instead of forcing node.js to wait, or call another callback inside the callback where you need the result of r.

您应该将回调设置为嵌套,而不是强制节点。要等待,或在回调中调用另一个回调,在这个回调中,您需要r的结果。

Chances are, if you need to force blocking, you're thinking about your architecture wrong.

很有可能,如果您需要强制阻塞,那么您正在考虑您的体系结构错误。

#8


1  

supposing you have a function:

假设你有一个函数:

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


};

you can make use of callbacks like this:

你可以使用这样的回调:

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

#9


1  

To me it worked to use

对我来说,它是有用的。

JSON.parse(result)['key']

on the result I expected. This may not be "super general", but in the end "JSON.parse" managed the waiting for the asyncronous call, whilst result['key'] didn't.

就我所期望的结果。这可能不是“超级通用”,但最终是“JSON”。解析“管理等待异步调用的过程,而结果['key']则没有。

#10


0  

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

function renderDbtest(result, res){
  res.render('db', { result: result });
}

Here is how I did it, you just have to pass "res" with it, so you can render later

我是这样做的,你只需要通过“res”,你就可以在后面渲染了。