简体   繁体   中英

Database call in a function without callback with Node.js and Postgresql

I'm trying out the framework node.js on one of my projects. I'm really seeing some good advantages on what they called "event-driven, non-blocking I/O model" however if my project there are some moments where I don't necessarily want to have some asynchronous calls and to be able to several operation before launching some asynchronous call.

Especially when I want to do some factorization and create some functions.

Typically I have the following case: I know that in several part of my program I have to check if a media is existing in my database for a given string or id.

So as a guy who tried to stay organize I want to create a function that I will call each time I need to check this. However, I did not find the way to do that with node.js and pg (the npm PostgreSQL library ( https://github.com/brianc/node-postgres/ ) . Indeed, there is always a callback in the function and the return is null because of the callback. Here is an example below

/*
    Function which is supposed to check if a media existing
 */
function is_media_existing (url_or_id){
    log.debug("is_media_existing : begin of the function", {"Parameter" : url_or_id});
    pg.connect(connectionstring, function (err, client, done) {
        if (err) {
            log.warning("is_media_existing : Problem with Database connection", {
                "Parameter": url_or_id,
                "Error": err
            });
        }
        if (isNaN(url_or_id)) {
            // Case is parameter is not a number (string)
            var query = client.query('SELECT COUNT(*) as count FROM media WHERE url = $1::string ', url_or_id);
            query.on('error', function (error) {
                log.warning("is_media_existing : Problem with Database query (connection to db passed but not query " +
                    "", {"Parameter": url_or_id, "Error": error});
            });
            return query;
        } else {
            // Case is parameter is a int
            log.debug("is_media_existing : Type of Parameter is a string");
            // Case is parameter is not a number (string)
            var query = client.query('SELECT COUNT(*) as count FROM media WHERE id = $1::id ', url_or_id);
            query.on('error', function (error) {
                log.warning("is_media_existing : Problem with Database query (connection to db passed but not query " +
                    "", {"Parameter": url_or_id, "Error": error});
            });
            return query;
        }
    });
}


// Executing the function 
var test = is_media_existing("http://random_url_existing_in_db");
// test is always null as the return is in a callback and the callback is asynchronous 

i have the feeling my question is touching the core concepts of node.js, and perhaps my approach is wrong and I apologize in advance. I know it's not good to wait for a response before doing something. But what's the alternative? How can I factorize my code into functions when I need some functionalities in several part of my code?

So if there would be anyone who could explain how to do that with a best practice of programming it would be great.

Thanks

Anselme

As Cody says, you probably dont want to do synchronous function.

The way you should handle the situation in your example is to pass in your own callback like this

function is_media_existing (url_or_id, callback){

and then instead of return query; use your callback like this-

callback(query);

or probably better to follow the node convention for callback functions to have two parameters (err, result) so your callback would look like this

callback(null, query);

Here is a rework of your sample

function is_media_existing (url_or_id, callback){       /* callback(err, result) */
    log.debug("is_media_existing : begin of the function", {"Parameter" : url_or_id});
    pg.connect(connectionstring, function (err, client, done) {
        if (err) {
            done(err);
            log.warning("is_media_existing : Problem with Database connection", {
                "Parameter": url_or_id,
                "Error": err
            });
            return callback(err, null);     
                /* note that this return is simply used to exit the connect's callback and the return value is typically 
                 * not used it is the call to callback() that returns the error value */
        } 
        var qrystr;

        if (isNaN(url_or_id)) {
            log.debug("is_media_existing : Type of Parameter is a string");
            qrystr = 'SELECT COUNT(*) as count FROM media WHERE url = $1::string;';
        } else {
            qrystr = 'SELECT COUNT(*) as count FROM media WHERE id = $1::id;';
        }
        client.query(qrystr, [url_or_id], function(err, result){
            done();
            if(err){
                /* .. */
            }
            callback(err, result);
        });
    });
}


// Executing the function 
var test = is_media_existing("http://random_url_existing_in_db", function(err, result){
    if(err){

    }else {

    }
});

If you end up with a hard nest of callbacks, promises are really worth looking into.

I don't think you really do want a synchronous call. The problem with synchronous calls in node is that it stops the entire process from doing anything while a synchronous function is running as it will stop the event loop. As an example lets say your sync function takes 2 seconds to complete. Your server will then do nothing for 2 full seconds. That 2 seconds includes everything (accepting new connections, everything else, etc). The reason blocking functions don't exist is because they are ( very ) bad. Here is an example how your function will react in an async manor.

is_media_existing("http://random_url_existing_in_db", function(exists){
  if (exists){
    //do stuff
  } else {
    //do this other stuff
  }
});

Then within is_media_existing you will need to call that callback function when your query completes.

//psuedo
function is_media_existing(url, callback){
  query('select COUNT(*) as count FROM media WHERE id = $1::id '. [url], function(err, result){
    if (err)
      callback(false)
    else
      callback(result.count > 0)
  })
}

With the new ES6 plus async stuff and babel its simpler. You can npm i -g babel npm i babel-runtime then compile and run the following with babel test.js --optional runtime --stage 2 | node babel test.js --optional runtime --stage 2 | node . Please read the following example carefully to see how to adapt it to your use case:

let testData = [                                                
  { id: 0, childIds: [1,2]},                                    
  { id: 1, childIds:[] }                                   
];                                                              

function dbGet(ids) {                                           
  return new Promise( r=> {
    // this an example; you could do any db
    // query here and call r with the results                                    
    r(ids.map((id) => { return testData[id];}));
  });                                                           
}

async function dbExists(ids) {                              
  let found = await dbGet(ids); 
  return (found && found.length>0);                                               
}                                                               

async function test() {                                         
  var exists = await dbExists([0]);                              
  console.log(exists);                      
}                                                               

test().then(f=>{}).catch( e=> {console.log('e',e)});            

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM