【问题标题】:Sails.js best practice in using transactions with promises (Postgres)Sails.js 使用带有承诺的事务的最佳实践(Postgres)
【发布时间】:2017-04-03 06:12:13
【问题描述】:

我正在使用带有 Postgres 的 Sails 0.9.16,我的问题是:使用当前 API 和 Promise 执行事务的最佳方式是什么?可能有比以下更好的东西:

    Model.query('BEGIN TRANSACTION', function (err) {
      if (err) {
        next(err);
      } else {
        Model
          .create(...)
          .(function (value) {
            return [value, RelatedModel.create(...).then(...)];
          })
          .fail(function (err) {
            Model.query('ROLLBACK');
            next(err);
          })
          .spread(function (...) {
            Model.query('COMMIT')
            next(...);
          })
      }
    })

感谢您的帮助!

【问题讨论】:

  • 嘿,您是如何解决问题的,或者哪种方法最适合您?
  • 由于 Sails postgres 适配器为每个查询拉/创建新连接,我最终修改了适配器:gist.github.com/zuker/62cd680d58a8f218e87d

标签: javascript node.js sails.js waterline


【解决方案1】:

我目前正在使用这个确切的工作流程。要使用 Promise 执行一个查询,请执行以下操作:

Model
 .query(params)
 .then(function(result){
 //act on result
 })
 .catch(function(error){
 //handle error
 })
 .done(function(){
 //clean up
 });

要并行执行多个查询,请执行以下操作:

var Promise = require('q');

Promise.all([

    User.findOne(),
    AnotherModel.findOne(),
    AnotherModel2.find()

])
.spread(function(user,anotherModel,anotherModel2){
    //use the results
})
.catch(function(){
    //handle errors
})
.done(function(){
    //clean up
});

如果您想避免在代码中嵌套:

Model
.query(params)
.then(function(result){//after query #1
    //since you're returning a promise here, you can use .then after this
    return Model.query();
})
.then(function(results){//after query#2
    if(!results){
        throw new Error("No results found in query #2");
    }else{
        return Model.differentQuery(results);
    }

})
.then(function(results){
//do something with the results
})
.catch(function(err){
    console.log(err);
})
.done(function(){
    //cleanup
});

注意:目前,waterline 使用 Q 表示 promise。这里有一个将水线从 Q 切换到 bluebird 的拉取请求:waterline/bluebird

当我回答这个问题时,我还没有在大学上过数据库课,所以我不知道什么是事务。我做了一些挖掘工作,bluebird 允许您使用承诺进行交易。唯一的问题是,这并没有完全内置在风帆中,因为它是一个特殊的用例。这是 bluebird 针对这种情况提供的代码。

var pg = require('pg');
var Promise = require('bluebird');
Promise.promisifyAll(pg);

function getTransaction(connectionString) {
    var close;
    return pg.connectAsync(connectionString).spread(function(client, done) {
        close = done;
        return client.queryAsync('BEGIN').then(function () {
            return client;
        });
    }).disposer(function(client, promise) {
        if (promise.isFulfilled()) {
            return client.queryAsync('COMMIT').then(closeClient);
        } else {
            return client.queryAsync('ROLLBACK').then(closeClient);
        }
        function closeClient() {
            if (close) close(client);
        }
    });
}

exports.getTransaction = getTransaction;

【讨论】:

  • 你能给蓝鸟等价物吗,因为水线已经转移到蓝鸟了
  • @lujaw 对应的 bluebird 代码应该与它们都遵守 A+ 承诺规范完全相同。
  • @aclave1 在第三个示例中,如何将第二个then 中的两个对象或变量返回到第三个then。我的意思是第一个查询的结果和第二个查询的另一个结果,并在第三个then 中接收这些结果?我希望稍微解释一下。提前致谢!
  • @Lexynux 您需要查看 Bluebird 的 Promise.Join(result1,result2).spread(function(result1,result2){ }) 或 Promise.bind() ,具体取决于您的用例
  • 你有什么例子吗?我的代码看起来像这样,它不起作用!
【解决方案2】:

处理事务的最佳方式是当它们被promise 库正确包装时,因为事务逻辑完美地映射到promise 事件链中,不必担心何时执行COMMITROLLBACK ,因为它会自动发生。

这是一个完整的示例,说明它如何与 pg-promise 库一起使用:

var pgp = require('pg-promise')(/*options*/);

var cn = "postgres://username:password@host:port/database";
var db = pgp(cn); // database instance;

db.tx(t => {
    // BEGIN has been executed
    return t.batch([
        t.one("insert into users(name) values($1) returning id", 'John'),
        t.one("insert into users(name) values($1) returning id", 'Mike')
    ]);
})
    .then(data => {
        // COMMIT has been executed
        console.log(data[0].id); // print id assigned to John;
        console.log(data[1].id); // print id assigned to Mike;
    })
    .catch(error => {
        // ROLLBACK has been executed
        console.log(error); // print why failed;
    });

【讨论】:

  • 这看起来像是在 SailsJS 中使用的很棒的逻辑,但是我怎么知道什么时候使用 Promise 以及什么时候使用简单代码?
  • 通过简单的代码 - 你是指回调吗?代码和库的异步特性决定了应该使用什么。
  • 但是我已经读到,promise 是最常用的,我们需要许多嵌套回调才能获得干净的代码。是真的吗?
  • @AlexVentura 不,promises 是 Node.js 中异步编程的新标准。像 ES6 生成器和 ES7 async/await 这样的新东西会自动使用 Promise 标准。见examples
  • 好吧,总之比回调更好用?
猜你喜欢
  • 2018-10-12
  • 1970-01-01
  • 2017-12-12
  • 1970-01-01
  • 2018-09-25
  • 2014-02-24
  • 2017-02-20
  • 2017-09-01
  • 1970-01-01
相关资源
最近更新 更多