0

I have two problems implementing a RESTful service using Node.js / node-postgres lib / PostgreDB and both are due to the async nature of JS.

A) I need to pass an extra argument to a callback in client.query(query, callback) call

I am inside a callback of a query and going through an array of recently fetched rows from a DB and want to launch a subsequent query for each of them:

var query =  client.query('SELECT * FROM event', queryAllEventsHandler);
function queryAllEventsHandler(err, result){        
   allEvents = result.rows;

   /* allEvents is an JSON array with the following format
   [  {"id_event":1, "name":"name of the event"}, 
      {"id_event":1, "name":"name of the event"} 
   ]
   */

for(var i = 0; i<allEvents.length; i++){
     client.query('SELECT * FROM days where id_event = $1',[allEvents[i].id_event], function( err, result){
               //I want to have a reference to variable i
     }
}

In the above example I want to do something like:

client.query('SELECT * FROM days where id_event = $1',[allEvents[i].id_event], function( AN_EXTRA_ARG, err, result)

Where the AN_EXTRA_ARG is an extra argument or a closure in the callback function... How can I achieve this? Should I create an closure with the of i and pass it as a callback's arg? How ? :|

B) "Synchronizing" queries

I need to launch various queries and create a custom JSON from all of them. Since every query and it's callback are asynchronous (waiting for no one) I was looking for a way to "tame" it and among other stuff I found a solution that occured to me in the first place, but seemed a bit "bad/lousy": Keeping the query count is really the way to go as @jslatts suggests in Synchronous database queries with Node.js?

Hope I

Community
  • 1
  • 1
luigi7up
  • 5,779
  • 2
  • 48
  • 58

4 Answers4

1

With regards to question A, you could create a function to handle both your queries and only return when the last query is executed and return both results to the callback.

for(var i = 0; i<allEvents.length; i++){
  query(client, allEvents[i], function(result1, result2) {
    //do something
  });   
}

function query(client, event, callback) {
    client.query('SELECT * FROM days where id_event = $1',[event.id_event], function( err1, result1){
        client.query('SELECT * FROM days where id_event = $1',[event.id_event], function( err2, result2){
          callback(result1, result2);
        });
     });
}
Rei Mavronicolas
  • 1,387
  • 9
  • 12
1

I don't like answering my on question, but this might be of interest to someone.... Regarding the A part of my question. You can assign a custom object to this in your function.

As you know a keyword this corresponds to the Window (top) object when inside a function (unless it's a method function). Using the bind function you can change the reference of this to your own object...

So what I did was, I created a named function queryCallback

function queryCallback(err, result){
        //this == Window (default)             
}

changed the anonymous callback function to the named one queryCallback:

client.query('SELECT * ... where id_event = $1',[allEvents[i].id_event], queryCallback.bind( {"position":i},  err, result));

Now, note queryCallback.bind( {"position":i}, err, result));

What bind(my_custom_this, [other args]) does is it binds a custom object (in my case {"position":i}) to this inside the function upon which the bind was called...

Now we have this scenario:

function queryCallback(err, result){
            //this == {"position":i}
}

Bind explained: http://fitzgeraldnick.com/weblog/26/

luigi7up
  • 5,779
  • 2
  • 48
  • 58
  • I guess this will work in this particular instance. But what if you want to access the original scope? You'd normally do `\.bind(this)`, but you've alread used that. I guess you could `bind({ position: i, context: this })` but it doesn't get prettier that way. – mtsr Jun 25 '13 at 13:23
0

A) I personally like lodash (or underscore if you prefer) partial() for this. It takes a function and a number of arguments and returns a function with the provided arguments applied and the remaining arguments still open. It's very much like the functional concept of currying.

B) For combining multiple asynchronous results I highly recommend async. The syntax will take a little getting used to, but makes thing like this very easy. Quick sample:

async.parallel([
    one: function(callback){
        db.fetch(options, callback);
    },
    two: function(callback){
        db.fetch(options, callback);
    }
],
function(err, results){
    // this callback will get called when either parallel call gives an error
    // or when both have called the callback
    if (err) {
        // handle error
        return;
    }

    // get the results from results.one and results.two
});

== Added in edit ==

Actually lodash also provides a nicer (imho) albeit slightly more expensive (due to function calls) solution for your problem A):

_(allEvents).each(function(event, index, array) {
    client.query('SELECT * FROM days where id_event = $1',[event.id_event], function( err, result) {
         // Just use 'index' here, which doesn't change during the each
     }
});
mtsr
  • 3,092
  • 1
  • 14
  • 21
  • Thanks for the tips... partial gave me an idea to use bind()... thanks.... Will surely look into async... I don't wanna count calls:) – luigi7up Jun 25 '13 at 12:01
0

For B), your options include async or via a Promise library (such as Q, when.js, Bluebird, etc...)

alphadogg
  • 12,762
  • 9
  • 54
  • 88