1

Please take into consideration that similar questions have been asked on SO and I went through most of them.

I am making a RESTful service that needs querying the DB to get the data. I wrote the code that queries the database correctly but does returns undefined all the time. The code is here:

function returnAll(){
    ModuleDBService.find({},function(err,data){
        if(err){
            console.log('Error occured while retrieving the documents!');
        }
        return data;
    });
}

I was exporting the module using:

module.exports = {
    getAll:returnAll
};

After digging SO a lot, I discovered that I will need to use callback to get the data. I went through many examples and tried to apply a similar technique to my code, the modified code looked like this:

function getAllFromDatabase(callback){
    ModuleDBService.find({},function(err,data){
        if(err){
            console.log('Error occured while retrieving the documents!');
        }
        callback(returnAll(data));
    });
}


function returnAll(data){ return data;}

and then returning it in the similar fashion as above.

But now I am getting error that ModuleDAO.getAll is not a function (I am using var ModuleDAO = require('path to the database service').

I tried many variations of the code, went through a couple of videos on YouTube, all of them either lead to returning undefined, or return to the above stated error. If anyone could fix the code and throw light on this whole callback thing (Or could provide solid documentation to understand it), it'll be a great help.

Thanks.

EDIT: After all the extremely helpful answers, here is a summary:

Callbacks cannot return data, pass the function (the callback function) that you want your program to call with the data. In my case, it was my router returning the data.

Here is the corrected code:

function returnAll(callback) {
    ModuleDBService.find({}, function (err, data) {
        if (err) {
            console.log("Error while retrieving the document!")
            callback(null);
        }
        callback(data);
    });
}

I used this code in my router as:

mainAPIRouter.post('/api/module', function (req, res) {
    try {
        moduleDAO.getAll(function(data){
            res.status(200);
            res.json(data);
        });
    } catch (error) {
        res.status(500);
        return res.send("Invalid request");
    }
});

Thanks to all those who helped! :)

metamemelord
  • 500
  • 1
  • 7
  • 19
  • 1
    To me this seems like an X/Y that's actually just another duplicate of : https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call – Kevin B Apr 18 '18 at 20:56
  • @KevinBHi! I understand that similar questions are around and I went through most of them, it's just I am not able to figure out why similar solutions lead to undefined and errors in my case. – metamemelord Apr 18 '18 at 21:02
  • What is undefined? – Kevin B Apr 18 '18 at 21:03
  • For example, returnAll doesn't return anything, and the reason is covered in the duplicate i linked. If that's what was undefined, then yeah, this is definitely a dupe. – Kevin B Apr 18 '18 at 21:04
  • It is in fact a duplicate of that bug @KevinB. – Pointy Apr 18 '18 at 22:15

3 Answers3

1

You are close. You don't need the returnAll() function, and you need to export getAllFromDatabase and pass a callback to it:

function getAllFromDatabase(callback){
    ModuleDBService.find({},function(err,data){
        if(err) {
            console.log('Error occured while retrieving the documents!');
        }
        callback(data);
    });
}

module.exports = {
    getAllFromDatabase: getAllFromDatabase
};

Then, when you want to use it, you need a callback function:

dataModule.getAllFromDatabase(callbackHandler);

function callbackHandler(dataFromDatabase) {
    // this function will be executed when ModuleDBService executes the callback
    console.log(dataFromDatabase);
}

A small detail: if err is Truthy, you should not execute the callback:

if(err) {
    console.log('Error occured while retrieving the documents!');
} else {
    callback(data);
}
raul.vila
  • 1,984
  • 1
  • 11
  • 24
  • 1
    good answer. one suggestion, it can help make the code more robust if you check to make sure callback is a function before trying to invoke it. `if(typeof callback==='function') callback(data);` – David784 Apr 18 '18 at 21:14
1

You want to simply call the callback() with the data you need as an argument. You are making things much more complicated by passing another function into the callback. Try something like:

function returnAll(callback) {
  ModuleDBService.find({}, function(err, data) {
    if (err) return callback(err)
    callback(null, data);
  });
}

returnAll(function(err, data)) {
// it's customary for callbacks to take an error as their first argument
  if (err) {
    console.log('Error occured while retrieving the documents!');
  } else {
    // use data here!!
  }
}
Mark
  • 90,562
  • 7
  • 108
  • 148
  • Thanks a lot. I will try. It seems to me that I just have to return the data in the else section now and export the module as getAll, is that right? – metamemelord Apr 18 '18 at 20:59
  • 2
    @metamemelord You can't really ever *return* data when you're dealing with async functions. Your callback is being called by the `returnAll()` function. If you return from your callback you are returning data to the function that just called it. You need to *use* the data in your callback or pass it to another function. – Mark Apr 18 '18 at 21:01
  • Okay. Thanks a lot. I will then call my router and export the data directly into json. All this is happening because of having multiple layers of logic. Thanks a lot for all the help. – metamemelord Apr 18 '18 at 21:06
1

As previous answers mentioned, you can use a callback. You can also use a promise if you prefer:

function returnAll(){
    return new Promise(function(resolve, reject) {
    ModuleDBService.find({},function(err,data){
        if(err){
            console.log('Error occured while retrieving the documents!');
                reject(err);
        }
          resolve(data);
    });
     });
}

You would then use something like this to access it:

returnAll()
  .then(data=> {console.log(data); })
  .catch(err=> { console.log(err); });

*Edit: since you want to use a callback, thought I'd add my $0.02 there as well. The most streamlined approach would be to just use the callback you're passing in with the ModuleDBService.find, without the ad-hoc function. But it's good to verify that callback actually is a function, and if not to make it one...makes your code more fault-tolerant.

function returnAll(cb){
    if(typeof cb!=='function') cb = function() {};
    ModuleDBService.find({},cb);
}
David784
  • 7,031
  • 2
  • 22
  • 29
  • 1
    Thanks for the help. I am right now struggling with callbacks. I would definitely learn promise as well, but after I learn callbacks. Thanks a lot though! – metamemelord Apr 18 '18 at 20:58
  • 1
    @metamemelord no worries...I added some code to show how you might do a callback in a little more streamlined manner. Just one thing to be aware of, callbacks can sometimes make code very difficult to read if you have several callback functions running in sequence. You can end up with a callback inside of a callback inside of a callback...often called "callback hell." It's why promises were invented; just something to be aware of. – David784 Apr 18 '18 at 21:13
  • 2
    @metamemelord you're welcome, happy coding! Also just a suggestion, but you might think about upvoting raul.vila's answer in addition to marking it as the solution. Upvote gives him an additional 10 rep points. – David784 Apr 18 '18 at 21:21
  • 1
    I have upvoted all the three answers. When I was upvoting, I observed that someone had downvoted all three; I am not sure why is that so. – metamemelord Apr 18 '18 at 21:23
  • Ah, sorry, good on ya, it's hard to tell when a downvote has cancelled an upvote, or if no vote has been applied. – David784 Apr 18 '18 at 21:30
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/169280/discussion-between-metamemelord-and-david784). – metamemelord Apr 18 '18 at 21:35