使用 Bluebird 手动承诺 pg.connect

Posted

技术标签:

【中文标题】使用 Bluebird 手动承诺 pg.connect【英文标题】:Manually promisifying pg.connect with Bluebird 【发布时间】:2014-07-15 20:56:44 【问题描述】:

我想promisify node-postgres 的pg.connect 方法以及回调中提供的内部connection.query 方法。

我可以.promisify后者,但我需要手动实现第一个(如果我在这里遗漏了什么,请解释)。

问题是,我不确定这段代码是否正确或应该改进?代码正在运行,我只是想知道我是否按原意使用 Bluebird。

// 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;
;

【问题讨论】:

就是这样,如果您没有使用提供自动连接和事务管理的 Promise 库,那么您实际上是在浪费关于数据库的 Promise 最好的部分。像 Bluebird 实现的正面承诺是一个很棒的通用工具,但它对使用数据库没有任何意义。 对于可能的未来读者,如果想知道为什么手动承诺可能是一个坏主意:1) 启动函数可能会抛出异常而被忽视 2) 结果回调可能会被多次调用。更多信息bytearcher.com/articles/pitfalls-of-promisifying-by-band 【参考方案1】:

扔掉所有可怕的回调代码,然后在应用程序初始化的某个地方执行此操作:

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);

稍后您可以在任何地方使用 pg 模块,就好像它被设计为使用 Promise 开头一样:

// 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();
         );
);

【讨论】:

感谢您的回复,@Esailija。我之前的代码和你的类似,但我不喜欢 .promisifyAll 的是 *Async 后缀。在 Node 中,方法不会在其名称中声明它们的“异步”,因为异步自然是默认值。我希望在我的代码中保持这种一致性。考虑到这一点,您是否建议替代我的手动包装? @luisfarzati 是的,自己构建 Bluebird 并将 AFTER_PROMISIFIED_SUFFIX 更改为您想要的任何内容。或者,拨打.promisify 而不是.promisifyAll @luisfarzati 异步后缀并不意味着异步,而是该方法返回一个承诺,这肯定不是节点中的默认值。一些后缀是必需的,选择 Async 是因为它与 C# 中用于返回 C# 承诺的方法的后缀相同。 @luisfarzati 也不确定您的计划,但如果您担心一致性,这意味着您计划混合回调和承诺,这是一种严重的反模式。 @luisfarzati 如果“异步”不适合您,我会更改后缀(我将在下一个版本中为此添加一个选项,但从未意识到有人可能会遇到问题)。您应该阅读公共接口的 api 文档,源代码中未记录的任何方法都是内部的或已弃用。【参考方案2】:

到目前为止,有许多库可以为您执行此操作:

pg-promise - PG 的通用 Promises/A+ postgres-bluebird dbh-ph pg-bluebird

【讨论】:

你知道 postgres-bluebird 的作者问了原来的问题吗? @SpencerRathbun:答案不仅适用于 OP,也适用于未来的访客。如果 OP 希望提供一个答案,提供 postgres-bluebird 的链接以获得信用,那将是合适的。 第一个答案是抛弃一种可怕的方法,并用另一种可怕的方法取而代之。当涉及到数据库使用时,promises 可以自动管理连接和事务的生命周期,如果做得好,你可以通过正面的 API 承诺得到任何东西。 @dman:这听起来像是 node-postgres 保持向后兼容性的普遍问题,与这些库无关。无论如何,欢迎您提供自己的答案。我不再使用 node.js,所以不会更新我自己的答案。【参考方案3】:

我建议稍微修改一下 Petka Antonov 解决方案

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);

这里 'pg[key] 包裹在 try-catch 块中,因为 pg[key] 可以在尝试访问 pg['native'] 时重新运行 error

【讨论】:

【参考方案4】:

bluebird 3 更新

pg.connectAsync(...).spread(function(connection, release) ... ) 调用将不再起作用,因为 bluebird 的 API 已更改:http://bluebirdjs.com/docs/new-in-bluebird-3.html#promisification-api-changes。

问题在于 bluebird 3 中的 promisifyAll 默认情况下不处理多个参数。这会导致 .spread() 调用报告 TypeError,如下所示:

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

要解决此问题,您可以为 connect / connectAsync 显式启用多个参数。在上面提到的所有有希望的东西之后执行以下操作:

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

【讨论】:

以上是关于使用 Bluebird 手动承诺 pg.connect的主要内容,如果未能解决你的问题,请参考以下文章

像在 Q 中定义空的 Bluebird 承诺

无法得到我在 nodejs / mongoose / bluebird 中返回的承诺

非 Promise 值的“等待”无效(Bluebird 承诺)

在Bluebird中刷新所有已解决的承诺

与 fs 和 bluebird 的承诺

如何使用 Bluebird 在构造函数构建的“类”上承诺导出的函数