Manually promisifying pg.connect with Bluebird

2019-01-23 09:24发布

问题:

I want to promisify node-postgres' pg.connect method along with the inner connection.query method provided in the callback.

I can .promisify the latter, but I need to implement the first one manually (if I'm missing something here, please explain).

The thing is, I'm not sure if this code is correct or should be improved? The code is working, I just want to know if I'm using Bluebird as meant.

// aliases
var asPromise = Promise.promisify;

// save reference to original method
var connect = pg.connect.bind(pg);

// promisify method
pg.connect = function (data) {
  var deferred = Promise.defer();

  connect(data, function promisify(err, connection, release) {
    if (err) return deferred.reject(err);

    // promisify query factory
    connection.query = asPromise(connection.query, connection);

    // resolve promised connection
    deferred.resolve([connection,release]);
  });

  return deferred.promise;
};

回答1:

Throw all that horrible callback code away, then do this somewhere in your application initialization:

var pg = require("pg");
var Promise = require("bluebird");

Object.keys(pg).forEach(function(key) {
    var Class = pg[key];
    if (typeof Class === "function") {
        Promise.promisifyAll(Class.prototype);
        Promise.promisifyAll(Class);
    }
})
Promise.promisifyAll(pg);

Later in anywhere you can use the pg module as if it was designed to use promises to begin with:

// Later
// Don't even need to require bluebird here
var pg = require("pg");
// Note how it's the pg API but with *Async suffix
pg.connectAsync(...).spread(function(connection, release) {
     return connection.queryAsync("...")
         .then(function(result) {
            console.log("rows", result.rows);
         })
         .finally(function() {
            // Creating a superfluous anonymous function cos I am
            // unsure of your JS skill level
            release();
         });
});


回答2:

By now there are a number of libraries which do this for you:

  • pg-promise - generic Promises/A+ for PG
  • postgres-bluebird
  • dbh-ph
  • pg-bluebird


回答3:

Update for bluebird 3:

The pg.connectAsync(...).spread(function(connection, release) { ... }) call will not work anymore, because the API of bluebird has changed: http://bluebirdjs.com/docs/new-in-bluebird-3.html#promisification-api-changes .

The problem is that promisifyAll in bluebird 3 does not handle multiple arguments by default. This results in the .spread() call reporting a TypeError like the following:

TypeError: expecting an array or an iterable object but got [object Null]

To solve this, you can explicitly enable multiple arguments for connect / connectAsync. Do the following after all the promisifying stuff mentioned above:

...
pg.connectAsync = Promise.promisify(pg.connect, { multiArgs: true });


回答4:

I suggest to modify Petka Antonov solution a bit

var Promise = require('bluebird');
var pg = require('pg');
Object.keys(pg).forEach(function (key) {
  var Cls = null;
  try {
    Cls = pg[key];
    if (typeof Cls === 'function') {
      Promise.promisifyAll(Cls.prototype);
      Promise.promisifyAll(Cls);
    }
  } catch (e) {
    console.log(e);
  }
});
Promise.promisifyAll(pg);

here 'pg[key] wrapped up in try-catch block because pg[key] can retrun error when attempt to access pg['native']