0

I need to return a value from a function, which gets its information from a query. I'm having some trouble passing information from the query's anonymous function to the container function.

I tried creating an array in a higher scope (the container function) and having the query function write to the array per result, but that didn't seem to work. I also tried passing the array into the query's anonymous function. That did not appear to work either.


Here's my function (executed within Node):

function retrieveSales(connection,timeFrame) {

    var sales = new Array();

    connection.query('select * from sales_entries where date BETWEEN ? AND ?',
    timeFrame,

    function (err, rows, fields, sales) {

        if (err) return callback(new Error('Failed to connect'), null);

        connection.end();

        for (x = 0; x < rows.length; x++) {
            sales.push(rows[x]);
        }

    });

    console.log('Sales Returned: ' + JSON.stringify(sales, null, 4));

    return sales;

}

which result in 'cannot call method 'push' of undefined.

How should I properly write to the sales array so that I can return it with retrieveSales()?

  • `connection.query()` is **asynchronous** ! – adeneo Mar 24 '14 at 23:49
  • So, then, I **can't** return a value from it? How should I achieve a result that will allow me to pass the returned value to another function? –  Mar 24 '14 at 23:50
  • async strikes again... yay – Hogan Mar 24 '14 at 23:51
  • the `sales` your function takes as a parameter is overriding the `sales` you've defined at a higher scope. – Red Alert Mar 24 '14 at 23:51
  • @RedAlert it seems as if even with that fixed, my problem will not be solved, because the data will not be pushed to my array until *after* the function completes, and has already returned a null value. Which is quite useless. –  Mar 24 '14 at 23:53
  • Like this -> http://jsfiddle.net/adeneo/5rvWp/ – adeneo Mar 24 '14 at 23:55
  • that's true, you need to approach the problem differently. It sounds like you want to block on `connection.query`, which is not really a good way to go about this. Instead of returning `sales`, you should have a function that does everything you want to do with `sales`, and give that as the callback for `connection.query`. – Red Alert Mar 24 '14 at 23:56
  • @RedAlert which is precisely what it appears adeneo has demonstrated. –  Mar 24 '14 at 23:57
  • 1
    @jt0dd - you probably want a promise here -- node supports them and it will make you code look nice. http://howtonode.org/promises – Hogan Mar 25 '14 at 00:08
  • Please see [How to return the response from an AJAX call?](http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-ajax-call) for an explanation of the problem and solutions. This applies to all asynchronous code, not only Ajax. – Felix Kling Mar 25 '14 at 00:09
  • @Hogan +1 for the discovery of fire. Well.. my discovery of promises, which will probably be far more useful than fire. –  Mar 25 '14 at 00:20

2 Answers2

0

You are dealing with JavaScript's beautiful asynchronous-ness-icity. You are are trying to return out of an asynchronous method. Try to brush up on how asynchronous programming works, and it'll make your life much, much easier; it isn't very hard, just different. Check out this code sample, hopefully it can get you up and going.

// use this as your callback to handle the results from connection.query()
function sales_cb(rows) {
  var sales = [];
  for (x = 0; x < rows.length; x++) {
    sales.push(rows[x]);
  }
  console.log('Sales Returned: ' + JSON.stringify(sales, null, 4));
}

function retrieveSales(connection, timeFrame) {
  // connection.query() is asynchronous - interact with it accordingly
  connection.query('select * from sales_entries where date BETWEEN ? AND ?', timeFrame,
    function (err, rows, fields, sales) {
      if (err) {
        callback(new Error('Failed to connect'), null);
      } else {
        // invoke sales_cb(sales) once the for loop is done
        sales_cb(rows);
      }
    });
}

Let me know if this helps :)

zahid
  • 83
  • 1
  • 8
0

Using:

https://github.com/kriskowal/q

Here is how to do it with a promise:

var q = require('q');


function retrieveSales(connection,timeFrame) {

  var sales = new Array();
  var deferred = q.defer();

  connection.query('select * from sales_entries where date BETWEEN ? AND ?',
     timeFrame,

     function (err, rows, fields) {

     if (err) return callback(new Error('Failed to connect'), null);

     connection.end();

      for (x = 0; x < rows.length; x++) {
          sales.push(rows[x]);
      }

      deferred.resolve(sales);
  });

  deferred.then(function(salesback) {
    console.log('Sales Returned: ' + JSON.stringify(salesback, null, 4));
  }

  // added bonus -- the calling function can also use the promise
  return deferred;

}

more info: http://howtonode.org/promises

Hogan
  • 69,564
  • 10
  • 76
  • 117
  • 1
    While I've chosen *zmhr's answer which I selected for it's straightforward nature, fixing the problem and answering my question beautifully, I'm interested in this approach as well. Are there any notable advantages to using promises in my situation over using the method detailed in the selected answer? –  Mar 25 '14 at 00:28
  • Put simply, is the difference merely based on preference? –  Mar 25 '14 at 00:29
  • 1
    @jt0dd - Yes, like most problems in programming there are many routes to a solution, some are faster, some are more elegant, some are more fun, and some are just wrong. zmhr's answer is good -- mine is the sexy new tech. You pick :D – Hogan Mar 25 '14 at 00:35
  • You have 27.6k rep. I'm going with zmhr, because underdogs and lazy people rock! However, seriously, that fancy new tech might just be the way to go after I'm done with my first app. Thanks and +1! –  Mar 25 '14 at 00:43
  • @jt0dd - I just re-read and realized I did not answer a question in your first comment. The use-case where promises shine is not so much for this example, but in cases where you want many different functions to trigger on resolve. You can see this in my example where I log to the console and return the promise for the calling function to also trigger. If you don't have this kind of use case then use zmhr's answer since it is simpler and clearer for the simple case. – Hogan Mar 25 '14 at 00:45
  • However, you have shown me something new. Thanks nonetheless! –  Mar 25 '14 at 00:46
  • It turns out that these promises are exactly what I need, because I need to perform 4 queries to build my message body, not one as shown here. –  Mar 25 '14 at 20:29
  • @jt0dd - Often I see this implemented with a promise on each item which is "watched" by another promise and this one resolves when the others finish (or fails when any of them fail.) – Hogan Mar 25 '14 at 20:31