3

so I'm having real trouble with this - been stuck on it for weeks.

My first function queries a MySQL database and returns a response, which then gets processed in my second function.

The issue is that JS is single-threaded and I can't figure out how to get it to run in the order I want:

  1. Function 1 is fired.
  2. The function fetches the DB response and is then processed.
  3. The reponse is handed to function 2, which then does more processing.

However at the moment, the program jumps straight to function two without allowing time for the query, and the function ends up returning 'undefined'.

var test = functionOne (functionTwo);   
console.log(test);    //returning 'undefined'

function functionOne(callback) {  
            //QUERY TAKES A LONG TIME
            client.query("[QUERY]", function(err, result) {
                    callback(null, result);
            });
}

function functionTwo(err, result) {
           //More processing - slightly slow
           return result;
}

EDIT:

Here is the full file.

    var client = require('./dbHelper');
    var convert = require('./genGIF');
    var exportPPM = require('./makePPM');
    var fs = require('fs');

    module.exports = function() {
            var test = queryDatabase (handleResult);
            console.log("print: " + test);
    }

    function queryDatabase(callback) {    
            //Query our database
            client.query("SHOW TABLES FROM mathsDB", function(err, result) {
                    if (err) {
                            callback(err);
                    }

                    else {                   
                            //Calculate the length of our table and then the last image in the table
                            var currentImage = result[result.length - 1]["Tables_in_mathsDB"];
                            currentImage = currentImage.substring(5);
                            callback(null, currentImage);
                            console.log(currentImage);
                    }
            });
    }

    function handleResult(err, currentImage) {
            fs.stat("./img/image" + currentImage + ".gif", function(err, stat) {
                    if (err==null) {
                            var imageFile = "/img/image" + currentImage + ".gif";
                            return imageFile;
                    }
                    else {
                            //Check if we have a .PPM file made for the image instead, then generate a .GIF
                            fs.stat("./img/image" + currentImage + ".ppm", function(err, stat) {
                                    if (err==null) {
                                            convert.convert("image" + currentImage);
                                            var imageFile = "/img/image" + currentImage + ".gif";
                                            return imageFile;
                                    }

                                    else {
                                            //Generate the .GIF if no .GIF or .PPM already.
                                            exportPPM.make();
                                            convert.convert("image" + currentImage);
                                            var imageFile = "/img/image" + currentImage + ".gif";
                                            return imageFile;
                                    }
                            });
                    }
            });
    }
  • The program you posted jumps straight to the `console.log`, but not into `functionTwo`. Are you sure this is what happens? – Bergi Apr 20 '16 at 12:42
  • You know that [`test` is expected to be `undefined`](https://stackoverflow.com/questions/23667086/why-is-my-variable-undefined-after-i-modify-it-inside-of-a-function)? – Bergi Apr 20 '16 at 12:45
  • @Bergi, thanks for the link, it did help to clarify the workings of asynchronicity. I understand that the test isn't assigned until after I ask it to print, and if I wanted it to print AFTER I've assigned it I could add the print into the callback function, however this same logic doesn't seem to hold for 'return'. How would I enable the program to return the correct value and not the undefined? – Chris Walker Apr 20 '16 at 12:53
  • [You cannot](http://stackoverflow.com/q/14220321/1048572). The callback runs after the function has returned. The only way is to let `handleResult` accept another callback, and call that when it's finished - just like you did with `queryDatabase`. Of course you can't pass `handleResult` directly to `queryDatabase` any more, you'd need to do `queryDatabase(function(err, result) { handleResult(err, result, function(test) { console.log(test); }); });` – Bergi Apr 20 '16 at 13:13

3 Answers3

2

You're returning your result as the first argument of your callback, but it's expected to be the second - in other words you're populating the err argument with your result, so the argument result will always be undefined.

So change:

function functionOne(callback) {  
            //QUERY TAKES A LONG TIME
            client.query("[QUERY]", function(err, result) {
                    callback(result);
            });
}

To this:

function functionOne(callback) {  
            //QUERY TAKES A LONG TIME
            client.query("[QUERY]", function(err, result) {
                    callback(null, result); // pass it as the second argument
            });
}

That should solve your issue.

jonny
  • 3,022
  • 1
  • 17
  • 30
  • Thanks for your help. You're right, thanks for pointing that out. However this didn't fix my result. I've edited my original question to reflect a little more testing I've done. – Chris Walker Apr 20 '16 at 12:37
  • @ChrisWalker callback functions aren't meant to return mate, that's meant for synchronous operations only - so `test` will almost certainly always be `undefined`. – jonny Apr 20 '16 at 12:54
  • Okay, thanks for the info. I think I might be structuring my program incorrectly. – Chris Walker Apr 20 '16 at 13:10
  • @AJS offers you the solution you're looking for - in async programming, you chain callbacks rather than returning, which is an inherently synchronous operation. This can often become kind of unsightly (unless you name each callback as he has done), so I'd recommend looking into using an async library like [`es6-promise`](https://github.com/stefanpenner/es6-promise) or [`async`](https://github.com/caolan/async) – jonny Apr 20 '16 at 13:15
  • Thanks! I had a look into using async however couldn't make it solve my issue, but then my issue was my misunderstanding of async programming ha. – Chris Walker Apr 20 '16 at 13:22
  • @ChrisWalker No problem - you should accept his answer if you have the solution you need. – jonny Apr 20 '16 at 13:26
1

just change

client.query("[QUERY]", function(err, result) {
                    callback(null, result);
            });

to :

client.query("[QUERY]", function(err, result) {
                    if( err) return callback(err);
                    return callback(null, result);
            });
Akram Saouri
  • 1,179
  • 8
  • 15
  • Or even simpler, `function(err, result) { callback(err, result); }` - or even simpler just `callback`… – Bergi Apr 20 '16 at 12:43
  • Thanks for your reply. I am using the code you've suggested - I've just cut it out of the question to help with readability. My issue isn't with processing the query - If I use a console.log, it prints the correct response. The issue is that the function isn't allowing enough time for the query to respond before returning, hence the 'undefined'. – Chris Walker Apr 20 '16 at 12:45
1

this should work:

  //start execution
  functionOne (functionTwo);   


  function functionOne(callback) {  
        //QUERY TAKES A LONG TIME
        client.query("[QUERY]", function(err, result) {
                callback(null, result);
        });
  }

  function functionTwo(err, result) {
       //call function done after long processing is finished with result  
       done(err,result);
  }

  function done(err,result){
       //do your final processing here
       console.log(result);
  }
AJS
  • 1,993
  • 16
  • 26