在 Node.js 中从回调函数返回一个值

我在 Node.js 中从回调函数返回值时遇到了一些小麻烦,我会尽量简单地解释我的情况。假设我有一个代码片段,它接受 URL 并点击该 URL 并给出输出:

urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
});

我尝试将它包装在一个函数中,然后返回一个如下值:

function doCall(urlToCall) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return finalData;
});
}

因为在我的 Node.js 代码中,我有很多 if-else语句,其中将决定 urlToCall的值,如下所示:

if(//somecondition) {
urlToCall = //Url1;
} else if(//someother condition) {
urlToCall = //Url2;
} else {
urlToCall = //Url3;
}

问题是,除了 urlToCall的值之外,urllib.request中的所有语句都将保持不变。所以我需要把这些公共代码放到一个函数中。我尝试相同,但在 doCall将总是返回我 undefined。我试过这样做:

response = doCall(urlToCall);
console.log(response) //Prints undefined

但是如果我在 doCall()中打印值,它会完美地打印出来,但是它总是返回 undefined。根据我的研究,我知道我们不能从回调函数返回值!(这是真的吗) ?如果是的话,有没有人可以告诉我如何处理这种情况,因为我想防止重复的代码在每个 if-else块。

236661 次浏览

Its undefined because, console.log(response) runs before doCall(urlToCall); is finished. You have to pass in a callback function aswell, that runs when your request is done.

First, your function. Pass it a callback:

function doCall(urlToCall, callback) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return callback(finalData);
});
}

Now:

var urlToCall = "http://myUrlToCall";
doCall(urlToCall, function(response){
// Here you have access to your variable
console.log(response);
})

@Rodrigo, posted a good resource in the comments. Read about callbacks in node and how they work. Remember, it is asynchronous code.

I am facing small trouble in returning a value from callback function in Node.js

This is not a "small trouble", it is actually impossible to "return" a value in the traditional sense from an asynchronous function.

Since you cannot "return the value" you must call the function that will need the value once you have it. @display_name already answered your question, but I just wanted to point out that the return in doCall is not returning the value in the traditional way. You could write doCall as follow:

function doCall(urlToCall, callback) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
// call the function that needs the value
callback(finalData);
// we are done
return;
});
}

Line callback(finalData); is what calls the function that needs the value that you got from the async function. But be aware that the return statement is used to indicate that the function ends here, but it does not mean that the value is returned to the caller (the caller already moved on.)

If what you want is to get your code working without modifying too much. You can try this solution which gets rid of callbacks and keeps the same code workflow:

Given that you are using Node.js, you can use co and co-request to achieve the same goal without callback concerns.

Basically, you can do something like this:

function doCall(urlToCall) {
return co(function *(){
var response = yield urllib.request(urlToCall, { wd: 'nodejs' }); // This is co-request.
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return finalData;
});
}

Then,

var response = yield doCall(urlToCall); // "yield" garuantees the callback finished.
console.log(response) // The response will not be undefined anymore.

By doing this, we wait until the callback function finishes, then get the value from it. Somehow, it solves your problem.

Example code for node.js - async function to sync function:

var deasync = require('deasync');


function syncFunc()
{
var ret = null;
asyncFunc(function(err, result){
ret = {err : err, result : result}
});


while((ret == null))
{
deasync.runLoopOnce();
}


return (ret.err || ret.result);
}