104

I am facing small trouble in returning a value from callback function in Node.js, I will try to explain my situation as easy as possible. Consider I have a snippet, which takes URL and hits that url and gives the output:

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

I tried to wrap it inside a function and return a value like this:

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

Because in my Node.js code, I have a lot of if-else statement where value of urlToCall will be decided, like this:

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

The thing is all of the statements inside a urllib.request will remain same, except value of urlToCall. So definitely I need to put those common code inside a function. I tried the same but in doCall will always return me undefined. I tried like this:

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

But if I print value inside doCall() it prints perfectly, but it will always return undefined. As per my research I came to know that we cannot return values from callback functions! (is it true)? If yes, can anyone advice me how to handle this situation, as I want to prevent duplicate code in every if-else blocks.

5
  • "is it true?" - yes, definitely. Commented Apr 28, 2014 at 11:32
  • @JanDvorak, so I don't have any option other than duplicating code? ;) Commented Apr 28, 2014 at 11:33
  • 1
    Would passing some of your own callbacks help? I believe so. Commented Apr 28, 2014 at 11:36
  • 6
    I think this link would help you understand how it works: github.com/maxogden/art-of-node#callbacks Commented Apr 28, 2014 at 11:41
  • 1
    @RodrigoMedeiros thanks for excellent resource. I am going through it, it helped me lot :) Commented Apr 28, 2014 at 11:54

4 Answers 4

156

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.

6
  • 23
    This works fine. But i have a question. in calling the doCall function indside that. How can i return the response variable? If for example i want to get that variable outside the function?
    – Romeo
    Commented Jul 19, 2016 at 1:37
  • 6
    @Romeo same question here!! Commented Apr 5, 2017 at 9:51
  • this post was very helpful, thank you! Commented Sep 7, 2018 at 21:20
  • 5
    Hi, Sorry if i'm misunderstanding but how is this any different? The urllib response is now just stuck inside the scope of another function outside of it. Its not like you can now use the response object anywhere outside of doCall?
    – alexr89
    Commented Jul 22, 2020 at 15:00
  • Very helpful. Always kind of avoided creating callbacks but I now understand. Thanks
    – StrayAnt
    Commented Sep 1, 2020 at 17:43
21

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

5

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

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.

4
  • but this is ecma6 soultion Commented May 18, 2016 at 7:21
  • and it also impact my performance Commented May 18, 2016 at 7:22
  • Is ecma6 an issue?
    – Ethan Yang
    Commented May 18, 2016 at 7:28
  • No that is not an issue but the problem is many of us still not using ECMA6 in their project as node.js has some problem with ECMA compatibility. Commented May 18, 2016 at 8:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.