【问题标题】:Firebase transactions in NodeJS always running 3 times?NodeJS 中的 Firebase 事务总是运行 3 次?
【发布时间】:2013-04-13 18:40:22
【问题描述】:

每当我在 NodeJS 中定义 Firebase 事务时,我注意到它总是运行三次——前两次使用空数据,最后第三次使用实际数据。这是正常/有意的吗?

例如这段代码:

firebaseOOO.child('ref').transaction(function(data) {
    console.log(data);
    return data;
});

输出以下内容:

null
null
i1: { a1: true }

我原以为它只打印最后一项。

在cmets中回答一个问题,这里同一个回调:

firebaseOOO.child('ref').transaction(function(data) {
    console.log(data);
    return data;
}, function(error, committed, snapshot) {
    if (error) 
        console.log('failed');
    else if (!committed)
        console.log('aborted');
    else 
        console.log('committed');
    console.log('fin');
});

产生以下输出:

null
null
i1: { a1: true }
committed
fin

在发布问题之前,我已经阅读了有关事务如何工作的详细信息,因此我尝试将 applyLocally 设置为 false,如下所示:

firebaseOOO.child('ref').transaction(function(data) {
    console.log('hit'); 
    return data; 
}, function(){}, false);

但它仍然命中 3 次(只是仔细检查)所以我认为这是不同的东西。在交易之前获得“价值”确实按预期“工作”,因为它只命中一次,这与 applyLocally 的设置无关,所以我不确定 applyLocally 是做什么的?这就是我在交易前获取价值的意思:

firebaseOOO.child('ref').once('value', function(data) {
    console.log('1');
    firebaseOOO.child('ref').transaction(function(data) {
        console.log('2');
        return data;
    });
});

输出:

1
2

@Michael:如何利用这种行为?事务主要是为了让数据使用自己来修改自己——典型的增量++场景。因此,如果我需要在现有值 10 上加 1,并继续使用 11 的结果,则函数命中的前两次我将得到需要处理的错误结果 1,最后是正确的结果第三次命中11。我怎样才能利用这两个最初的 1?另一种情况(也许我不应该为此使用事务,但如果它像我预期的那样工作,它会使代码更清晰)是插入一个值(如果它尚不存在)。如果事务只命中一次,则 null 值将意味着该值不存在,因此您可以在这种情况下将计数器初始化为 1,否则将 1 添加到任何值。对于嘈杂的空值,这是不可能的。

似乎从这一切中得出的结论是更频繁地使用“一次”模式?

一次交易模式:

firebaseOOO.child('ref').once('value', function(data) {
    console.log('1');
    firebaseOOO.child('ref').transaction(function(data) {
        console.log('2');
        return data;
    });
});

【问题讨论】:

  • 这对于事务来说是一个很奇怪的用法。为什么要将其设置回完全相同的值?如果你设置了第二个回调,它会说什么?提交是否成功?有错误吗?
  • 嘿加藤,这是证明我的问题的最简单的案例。我的真实代码有更多的逻辑。没有错误信息,最后记录成功。事实上,正如预期的那样,最后只有 1 条提交消息。我更新了我的问题以反映这些结果。

标签: node.js firebase


【解决方案1】:

您在此处看到的行为与 Firebase 如何触发本地事件并最终与 Firebase 服务器同步有关。在这个具体的例子中,“运行 3 次”只会在你第一次运行代码时发生——在那之后,状态已经完全同步,从那时起它只会触发一次。此行为在此处详细说明:https://www.firebase.com/docs/transactions.html(请参阅“运行事务时,会发生以下情况”部分。)

例如,如果您在同一位置有一个未完成的 on(),然后在稍后的某个时间运行相同的事务代码,您会看到它只运行一次。这是因为在事务运行之前一切都是同步的(在理想情况下;除非有任何正常的冲突等)。

【讨论】:

  • 如何区分由于您描述的内部事件导致的空值与 Firebase 中尚不存在对象的“真实”空值?
  • 您可以在同一位置为“value”事件添加侦听器,并在事务调用中将 applyLocally 设置为 false。这样,您的价值事件监听器只会在交易的最终价值被设置后触发。在此处查看交易文档,查找 applyLocally 参数:firebase.com/docs/javascript/firebase/transaction.html
  • 此外,我会对您的用例以及空值是否真的造成麻烦感兴趣。现在,我们在数据同步之前乐观地运行事务,这就是为什么您会看到“null”,并且到目前为止效果很好。但是我们已经考虑过改变行为。如果您有兴趣分享有关您的用例的更多详细信息,请在 firebase com 上 Ping michael!
  • 谢谢 Greg 和 Michael,我已经用 cmets/对您的问题/cmets 的回答更新了我的问题。迈克尔,我要给你发电子邮件,以便我们交换联系信息。
  • @MichaelLehenbauer 我在这里是因为我想运行一个安全的突变 - 仅当 x 已经存在时才更新 x.field。前两个空值使这不可能。嵌套/链接一次或一次,这很糟糕。此外,关于交易行为的信息也不再出现在新的 API 文档中
【解决方案2】:

transaction() 将被多次调用,并且必须能够处理空数据。即使您的数据库中存在现有数据,也可能在事务函数运行时不会在本地缓存。

firebaseOOO.child('ref').transaction(function(data) {

if(data!=null){
    console.log(data);
    return data;
}
else {
   return data;
 }
}, function(error, committed, snapshot) {
    if (error) 
        console.log('failed');
    else if (!committed)
        console.log('aborted');
    else 
        console.log('committed');
    console.log('fin');
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-14
    • 2019-06-27
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    • 2016-04-20
    • 1970-01-01
    相关资源
    最近更新 更多