Javascript 如何使用node.js使函数等待回调调用

Javascript 如何使用node.js使函数等待回调调用,javascript,multithreading,callback,node.js,Javascript,Multithreading,Callback,Node.js,我有一个简化的函数,如下所示: function(query) { myApi.exec('SomeCommand', function(response) { return response; }); } var returnValue = myFunction(query); myFunction(query, function(returnValue) { // use the return value here instead of like a regular

我有一个简化的函数,如下所示:

function(query) {
  myApi.exec('SomeCommand', function(response) {
    return response;
  });
}
var returnValue = myFunction(query);
myFunction(query, function(returnValue) {
  // use the return value here instead of like a regular (non-evented) return value
});
function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  var uvrun = require("uvrun");
  while (!r)
    uvrun.runOnce();
  return r;
}
//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();
}
基本上,我希望它调用
myApi.exec
,并返回回调lambda中给出的响应。但是,上面的代码不起作用,只是立即返回

只是为了一个非常粗俗的尝试,我尝试了下面的方法,但没有成功,但至少你知道我想要实现什么:

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

基本上,“node.js/event-driven”的好方法是什么?我希望我的函数等待回调函数被调用,然后返回传递给它的值。

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

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

如果您需要强制阻塞,那么您可能认为您的架构是错误的。

执行此操作的“good node.js/event-driven”方法是不要等待

与使用事件驱动系统(如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
  });
}
所以你不会像这样使用它:

function(query) {
  myApi.exec('SomeCommand', function(response) {
    return response;
  });
}
var returnValue = myFunction(query);
myFunction(query, function(returnValue) {
  // use the return value here instead of like a regular (non-evented) return value
});
function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  var uvrun = require("uvrun");
  while (!r)
    uvrun.runOnce();
  return r;
}
//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();
}
但就像这样:

function(query) {
  myApi.exec('SomeCommand', function(response) {
    return response;
  });
}
var returnValue = myFunction(query);
myFunction(query, function(returnValue) {
  // use the return value here instead of like a regular (non-evented) return value
});
function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  var uvrun = require("uvrun");
  while (!r)
    uvrun.runOnce();
  return r;
}
//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();
}

假设您有一个函数:

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);
}
});
选中此项:

您的wait.for代码:(需要生成器,--harmony标志)

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

有一个模块(针对较新的Nodejs版本更新),您可以在其中执行libuv主事件循环(即Nodejs主循环)的单个循环

您的代码如下所示:

function(query) {
  myApi.exec('SomeCommand', function(response) {
    return response;
  });
}
var returnValue = myFunction(query);
myFunction(query, function(returnValue) {
  // use the return value here instead of like a regular (non-evented) return value
});
function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  var uvrun = require("uvrun");
  while (!r)
    uvrun.runOnce();
  return r;
}
//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.runNoWait()
。这可以避免一些阻塞问题,但需要100%的CPU。)

请注意,这种方法使NodeJ的全部目的无效,即使所有内容都异步且无阻塞。此外,它可能会大大增加调用堆栈深度,因此最终可能导致堆栈溢出。如果递归地运行这样的函数,肯定会遇到麻烦

请参阅关于如何重新设计代码以使其“正确”的其他答案


此解决方案可能仅在您进行测试时有用,尤其是在您想要同步和串行代码时。

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

});

有关更多信息,请参阅:

如果您希望非常简单和容易,无需使用花哨的库,在执行其他代码之前,等待在节点中执行回调函数,如下所示:

function(query) {
  myApi.exec('SomeCommand', function(response) {
    return response;
  });
}
var returnValue = myFunction(query);
myFunction(query, function(returnValue) {
  // use the return value here instead of like a regular (non-evented) return value
});
function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  var uvrun = require("uvrun");
  while (!r)
    uvrun.runOnce();
  return r;
}
//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();
}
//初始化全局变量以控制回调状态
var callbackCount=0;
//调用具有回调函数的函数
someObj.executeCallback(函数(){
callbackCount++;
runOtherCode();
});
someObj2.executeCallback(函数(){
callbackCount++;
runOtherCode();
});
//调用必须等待的函数
continueExec();
函数continueExec(){
//这里是技巧,等待var callbackCount设置为回调函数的数量
if(回拨次数<2){
设置超时(continueExec,1000);
返回;
}
//最后,做你需要的
doSomeThing();
}

从节点4.8.0开始,您就可以使用ES6中名为generator的功能。 您可以按照此了解更深入的概念。 但基本上你可以使用发电机和承诺来完成这项工作。 我用它来预测和管理发电机

您的代码应该很好,如下面的示例所示

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

实现这一点的一种方法是将API调用包装成承诺,然后使用
wait
等待结果

// 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();
//假设这是一个包含两个回调的API函数,
//一个是成功,另一个是错误
函数API函数(查询、成功回调、错误回调){
如果(查询==“错误查询”){
errorCallback(“查询问题”);
}
successCallback(“您的查询是”);
}
//myFunction将上述API调用包装成承诺
//并使用resolve和reject处理回调
函数apiFunctionWrapper(查询){
返回新承诺((解决、拒绝)=>{
apiFunction(查询,(成功响应)=>{
解决(成功响应);
},(错误响应)=>{
拒绝(错误响应);
});
});
}
//现在可以使用wait从包装的api函数中获取结果
//您可以使用标准的try-catch来处理错误
异步函数businessLogic(){
试一试{
const result=wait apifonfictionwrapper(“查询所有用户”);
控制台日志(结果);
//下一行将失败
const result2=await-apiffunctionwrapper(“错误查询”);
}捕获(错误){
控制台错误(“错误:+错误”);
}
}
//调用main函数
businessLogic();
输出:

Your query was <query all users>
ERROR:problem with the query
您的查询失败
错误:查询有问题

使用async和wait更容易

router.post('/login',async (req, res, next) => {
i = await queries.checkUser(req.body);
console.log('i: '+JSON.stringify(i));
});

//User Available Check
async function checkUser(request) {
try {
    let response = await sql.query('select * from login where email = ?', 
    [request.email]);
    return response[0];

    } catch (err) {
    console.log(err);

  }

}

现在是2020年,API可能已经有了一个基于承诺的版本,可以与Wait一起使用。但是,某些接口,尤其是事件发射器,需要以下解决方法:

// doesn't wait
let value;
someEventEmitter.once((e) => { value = e.value; });
在这种情况下,它将是:

let response = await new Promise((resolve) => {
  myAPI.exec('SomeCommand', (response) => { resolve(response); });
});

wait在过去3年中一直出现在Node.js的新版本中(从v7.6开始)。

或者我在这里的做法是完全错误的,我应该调用另一个回调,而不是返回响应吗?在我看来,这是为什么忙循环不起作用的最好解释。不要试图等待。只需在回调结束时调用其中的下一个函数(依赖于回调)。我怀疑我有过这样的想法。很可能,我只想编写一个快速脚本到http.get()