any way to make nested callback code in javascript more readable?

consider this baby simple piece of code

pg.connect( {
    user: 'hhope',
    password: '...',
    port: 5432,
    host: '/var/run/postgresql',
    database: 'phiddler' },
  function( err, client ) {
    client.query(
      "select count(1) as ct from pg_prepared_statements",
      function( err, results ) {
        console.log( 'prepared statement count: '+results.rows[0].ct );
        client.query( {
                    name: 'test2' ,
                    text: "insert into t1( c2 ) values( 'q1')" },
          function( err, result ) {
            console.log( 'inserted t1' );
            client.query(
              "select count(1) as ct from pg_prepared_statements",
               function( err, results ) {
                  console.log( 'prepared statement count: '+results.rows[0].ct );
               } );
           }
        );
      }
    );
  }
);

if written procedurally would be

pg = new PG(...);
client = new pg.client();
console.log( client.col( "select ..." ) );
client.prepare( 'test2', "insert into t1( c2 ) values( 'q1')" );
console.log( client.col( "select ..." ) );

in my mind the latter is far more readable and maintainable.

is there some sort of compromise approach that would make the standard indented callback style more readable?

You want to use the async library's waterfall mechanism:

var async = require('async');

async.waterfall([
    pg.connect.bind(this, {
        user: 'user', password: 'pass', port: 5432, host: '/somewhere/', database: 'db'
    }),
    function(client, callback) {
        client.query('select * from foo', callback)
    }
], function(err, results) {
    console.log(results);
});

The bind method added to Functions in ES5 is very useful in combination with the async library, since often the last argument in the list is a callback, and the earlier values are often known before entering the chain.

The procedural variant isn't really equivalent - I assume its synchronous, whereas the original probably isn't.

If you don't need to close over variables, you can just use named functions. Something like:

function f2(){}
function f1() {
    client.query("select count(1) as ct from pg_prepared_statements", f2);
}
pg.connect( {
    user: 'hhope',
    password: '...',
    port: 5432,
    host: '/var/run/postgresql',
    database: 'phiddler' },
    f1
);

"Readable" is a matter of opinion, but you could do this:

var iter = function(cb) {
   var v = Array.prototype.slice.call(arguments);
   var f = function() {
        var g = v.shift();
        g && g(f);
   };

   f();
};

iter(function(cb) {
        console.log('in f1');
        cb();
     },
     function(cb) {
        console.log('in f2');
        cb();
     });