提问人:Chris 提问时间:2/16/2011 最后编辑:Jonathan HallChris 更新时间:9/13/2022 访问量:592943
如何使函数等到使用 node.js 调用回调
How to make a function wait until a callback has been called using node.js
问:
我有一个简化的函数,如下所示:
function(query) {
myApi.exec('SomeCommand', function(response) {
return response;
});
}
基本上我希望它调用 ,并返回回调 lambda 中给出的响应。但是,上面的代码不起作用,只是立即返回。myApi.exec
只是为了一个非常黑客的尝试,我尝试了以下不起作用的方法,但至少你明白了我想要实现的目标:
function(query) {
var r;
myApi.exec('SomeCommand', function(response) {
r = response;
});
while (!r) {}
return r;
}
基本上,什么是好的“节点.js/事件驱动”方法?我希望我的函数等到调用回调,然后返回传递给它的值。
答:
这违背了非阻塞 IO 的目的 -- 当它不需要阻塞时,你就要阻塞它:)
您应该嵌套回调,而不是强制 node.js 等待,或者在回调中调用另一个回调,其中需要 的结果。r
很有可能,如果你需要强制阻塞,你就把架构想错了。
评论
http.get()
console.log()
“好的节点.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
});
评论
fs
sync*
sync
假设你有一个函数:
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);
}
});
评论
检查一下: https://github.com/luciotato/waitfor-ES6
你的代码与 wait.for: (需要生成器, --harmony 标志)
function* (query) {
var r = yield wait.for( myApi.exec, 'SomeCommand');
return r;
}
注意:此答案可能不应在生产代码中使用。这是一个黑客,你应该知道它的含义。
有 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;
}
(您可以替代使用.这可以避免一些阻塞问题,但需要 100% 的 CPU。uvrun.runNoWait()
请注意,这种方法使 Nodejs 的整个目的无效,即让一切都异步和非阻塞。此外,它可能会大大增加您的调用堆栈深度,因此您最终可能会遇到堆栈溢出。如果你递归地运行这样的函数,你肯定会遇到麻烦。
请参阅有关如何重新设计代码以“正确”执行代码的其他答案。
这里的这个解决方案可能只在你做测试时有用,特别是想要同步和串行代码。
如果您不想使用回调,则可以使用“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
从节点 4.8.0 开始,您可以使用 ES6 的 generator 功能。 您可以按照本文了解更深入的概念。 但基本上你可以使用生成器和承诺来完成这项工作。 我正在使用 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));
实现此目的的一种方法是将 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 + ">");
}
// Next function 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
评论
async/await
使用 async 和 await 要容易得多。
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 很可能已经有一个基于 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); });
});
Await 在过去 3 年中一直在新的 Node.js 版本中(从 v7.6 开始)。
评论