Sails.js最佳实践:使用Promise进行事务处理(PostgreSQL)

18

我正在使用 sails 0.9.16 和 Postgres,我的问题是:使用当前的 API 和 promises 执行事务的最佳方法是什么?也许有比以下方式更好的方法:

    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(...);
          })
      }
    })

谢谢帮助!


嘿,你是怎么解决这个问题的?或者对你来说最好的方法是什么? - alexventuraio
由于Sails postgres适配器为每个查询拉取/创建新连接,我最终进行了适配器修改:https://gist.github.com/zuker/62cd680d58a8f218e87d - Zuker
2个回答

21

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

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。这里有一个拉取请求将 Waterline 从 Q 切换到 Bluebird:waterline/bluebird

当我回答这个问题时,我还没有上过大学的数据库课程,所以我不知道什么是事务。我查了一些资料,发现 Bluebird 可以让你使用 Promise 来处理事务。唯一的问题是,这不是 Sails 内置的功能,因为它是一种特殊的用例。以下是 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;

2
你能给出Bluebird的等效物吗?因为Waterline已经转向了Bluebird。 - Luja Shrestha
@aclave1 在第三个例子中,我如何在第二个“then”中返回两个对象或变量,以便在第三个“then”中接收第一个查询的结果和第二个查询的另一个结果?我希望能够解释清楚。提前致谢! - alexventuraio
你有任何例子吗?我的代码看起来像这样,但它不起作用! - alexventuraio
你能否为我发布一个问题并在评论中附上链接? - aclave1
@aclave1 这是问题链接:http://stackoverflow.com/questions/34641985/how-to-return-two-objects-in-a-promise-using-sails - alexventuraio
显示剩余2条评论

0
处理事务的最佳方式是使用承诺库正确地封装它们,因为事务逻辑完美地映射到承诺事件链中,无需担心何时执行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中使用的很棒的逻辑,但我如何知道何时使用promises,何时使用简单的代码? - alexventuraio
你所说的简单代码是指回调函数吗?你的代码和库的异步性决定了应该使用什么。 - vitaly-t
但是我读过的资料说,当你需要有许多嵌套回调函数以保持代码整洁时,Promise 是最常用的。这是真的吗? - alexventuraio
@AlexVentura 不是的,Promise 是 Node.js 中异步编程的新标准。而像 ES6 生成器和 ES7 async/await 这样的新特性自动与 Promise 标准配合使用。请参见示例 - vitaly-t
好的,那么在任何情况下都比回调函数更好用吗? - alexventuraio
@AlexVentura 没错。当你开始从许多方法中更改结果时,你会看到好处,因为使用回调函数很快就会变成一个难以阅读的嵌套噩梦。 - vitaly-t

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接