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

时间:2011-02-15 22:30:30

标签: javascript multithreading callback node.js

我有一个简化的功能,如下所示:

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

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

只是为了一次非常严厉的尝试,我尝试了以下哪些不起作用,但至少你明白了我想要实现的目标:

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

基本上,有什么好的'node.js / event driven'方式可以解决这个问题?我希望我的函数等到调用回调,然后返回传递给它的值。

13 个答案:

答案 0 :(得分:260)

“良好的node.js /事件驱动”方式是不等待

与使用节点等事件驱动系统时几乎所有其他内容一样,您的函数应该接受一个回调参数,该参数将在计算完成时调用。调用者不应该等待正常意义上的“返回”值,而是发送将处理结果值的例程:

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

答案 1 :(得分:23)

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

你的代码用wait.for :(需要生成器, - 和声标志)

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

答案 2 :(得分:15)

一种实现此目的的方法是将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

答案 3 :(得分:11)

如果您不想使用回叫,那么您可以使用&#34; Q&#34;模块。

例如:

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

答案 4 :(得分:9)

如果你想要它非常简单和容易,没有花哨的库,等待在节点中执行回调函数,在执行其他代码之前,就像这样:

!!!

答案 5 :(得分:5)

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

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

您的代码如下所示:

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

请注意,这种方法会使Nodej的整个目的无效,即使所有内容都异步和非阻塞。此外,它可能会大大增加您的callstack深度,因此最终可能会出现堆栈溢出。如果你递归地运行这样的函数,你肯定会遇到麻烦。

请参阅其他答案,了解如何重新设计代码以“正确”执行此操作。

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

答案 6 :(得分:4)

从节点4.8.0开始,您就可以使用ES6的称为生成器的功能。 您可以按照此article进行更深入的概念。 但基本上你可以使用发电机和承诺来完成这项工作。 我正在使用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));

答案 7 :(得分:1)

假设你有一个功能:

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

答案 8 :(得分:1)

对我来说,它可以使用

JSON.parse(result)['key']

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

答案 9 :(得分:1)

现在是 2020 年,API 可能已经有一个基于 Promise 的版本,可与 await 配合使用。但是,某些接口,尤其是事件发射器将需要此解决方法:

// doesn't wait
let value;
someEventEmitter.once((e) => { value = e.value; });
// waits
let value = await new Promise((resolve) => {
  someEventEmitter.once('event', (e) => { resolve(e.value); });
});

在这种特殊情况下,它将是:

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

过去 3 年(自 v7.6 起),Await 一直在新的 Node.js 版本中发布。

答案 10 :(得分:0)

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

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

我是这样做的,你只需要传递“res”,以便稍后渲染

答案 11 :(得分:-1)

这违背了非阻止IO的目的 - 当你不需要阻止时阻止它:)

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

如果你需要强行阻止,你可能会认为你的架构是错误的。

答案 12 :(得分:-1)

使用异步并等待它要容易得多。

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

  }

}