如果不同的条件为真,Firebase 如何用不同的状态/消息中断实时数据库事务?

Iva*_*hev 2 javascript firebase firebase-realtime-database

这是一个很好的做法,如果是的话,在不同情况下打破具有不同错误状态/消息的事务的正确方法什么

我有一个交易运行在一个“报价”条目上进行“座位”预订:

如果以下 3 个条件之一为真我想中断它并将状态/消息返回给调用者函数

  1. 如果询问用户已经预订了此优惠的座位。
  2. 如果没有足够的座位。
  3. 如果此优惠不存在。

如果一切正常,事务应该正常完成并将状态/消息返回到调用函数进行保留。

如果其中一个条件成立,我不确定如何中断交易。

  • 如果我使用throw new Error('description of the problem.')那么这将是一个异常,它不会被事务 Promise 的 catch() 处理程序处理,我不知道如何处理这个异常,因为它是一个异步函数这里。所以我认为我不应该使用异常。

这就是我的意思:

dealSeats = function(entryRef, data) {
    const TAG = '[dealSeats]: ';
    return entryRef.transaction((entry)=>{
        if (entry) {
            if ((entry.deals) && (entry.deals[data.uid])) {
                **? how to break the transaction with state/message 'You already have a deal.' ? and how to handle it below ?**
            } else if (entry.details.seatsCount >= data.details.seatsCount) {
                entry.details.seatsCount -= data.details.seatsCount;
                var deal = [];
                deal.status = 'asked';
                deal.details = data.details;
                if (!entry.deals) {
                    entry.deals = {};
                }
                entry.deals[data.uid] = deal;
            } else {
                **? how to break the transaction with state/message 'Not enought seats.' ? and how to handle it below ?**
            }
        }
        return entry;
        **? how to check if 'entry' is really null ? i.e. offer does not exists ?** and break and handle it.
    })
    .then((success)=>{
        return success.snapshot.val();
    })
    .catch((error)=>{
        return Promise.reject(error);
    });
}
Run Code Online (Sandbox Code Playgroud)

这是我在实时数据库中的数据:

activeOffers
 -LKohyZ58cnzn0vCnt9p
    details
        direction: "city"
        seatsCount: 2
        timeToGo: 5
    uid: "-ABSIFJ0vCnt9p8387a"    ---- offering user
Run Code Online (Sandbox Code Playgroud)

这是邮递员发送的我的测试数据:

{
 "data": 
  {
     "uid": "-FGKKSDFGK12387sddd",    ---- the requesting/asking user
     "id": "-LKpCACQlL25XTWJ0OV_",
     "details":
     {
         "direction": "city",
         "seatsCount": 1,
         "timeToGo": 5
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

==== 更新了最终来源 ====

非常感谢 Renaud Tarnec!

所以这是我工作正常的最终来源。如果有人看到潜在的问题,请告诉我。谢谢。

dealSeats = function(entryRef, data) {
    const TAG = '[dealSeats]: ';
    var abortReason;

    return entryRef.transaction((entry)=>{
        if (entry) {
            if ((entry.deals) && (entry.deals[data.uid])) {
                abortReason = 'You already made a reservation';
                return; // abort transaction
            } else if (entry.details.seatsCount >= data.details.seatsCount) {
                entry.details.seatsCount -= data.details.seatsCount;
                var deal = [];
                deal.status = 'asked';
                deal.details = data.details;
                if (!entry.deals) {
                    entry.deals = {};
                }
                entry.deals[data.uid] = deal;
                // Reservation is made
            } else {
                abortReason = 'Not enought seats';
                return; // abort transaction
            }
        }
        return entry;
    })
    .then((result)=>{ // resolved
        if (!result.committed) { // aborted
            return abortReason;
        } else {
            let value = result.snapshot.val();
            if (value) {
                return value;
            } else {
                return 'Offer does not exists';
            }
        }
    })
    .catch((reason)=>{ // rejected
        return Promise.reject(reason);
    });
}
Run Code Online (Sandbox Code Playgroud)

唯一的痛苦是在 VSCode 终端中部署期间通过不返回任何值来警告有关此中止的警告:

warning  Arrow function expected no return value  consistent-return
Run Code Online (Sandbox Code Playgroud)

目前我不确定我是否可以做些什么。

Ren*_*nec 5

在 Firebase API 参考文档中查看此文档:https ://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction

下面是来自这个文档的代码。看看如何return;用于中止事务(文档还说:“您通过不从更新函数返回值来中止事务”)。并注意onComplete()在事务完成时(在else if (!committed){} 内)调用的回调函数中如何处理这种特定情况。

// Try to create a user for ada, but only if the user id 'ada' isn't
// already taken
var adaRef = firebase.database().ref('users/ada');
adaRef.transaction(function(currentData) {
  if (currentData === null) {
    return { name: { first: 'Ada', last: 'Lovelace' } };
  } else {
    console.log('User ada already exists.');
    return; // Abort the transaction.
  }
}, function(error, committed, snapshot) {
  if (error) {
    console.log('Transaction failed abnormally!', error);
  } else if (!committed) {
    console.log('We aborted the transaction (because ada already exists).');
  } else {
    console.log('User ada added!');
  }
  console.log("Ada's data: ", snapshot.val());
});
Run Code Online (Sandbox Code Playgroud)

因此,恕我直言,您应该采用相同的模式,并在代码中询问“**?如何破坏交易”的地方采用相同的模式return;

更新:您可以使用变量区分流产案例,如下所示。如果您通过 Firebase 控制台添加age值 > 20 到的节点users.ada.name,第一个流产原因将被“触发”。

var adaRef = firebase.database().ref('users/ada');
var transactionAbortionCause;  //new variable
adaRef.transaction(function(currentData) {
  if (currentData === null) {
    return { name: { first: 'Ada', last: 'Lovelace' } };
  } else if (currentData.name.age > 20) {
    transactionAbortionCause = 'User ada is older than 20'; //update the variable
    console.log('User ada is older than 20');
    return; // Abort the transaction.
  } else {
    transactionAbortionCause = 'User ada already exists'; //update the variable
    console.log('User ada already exists');
    return; // Abort the transaction.
  }
}, function(error, committed, snapshot) {
  if (error) {
    console.log('Transaction failed abnormally!', error);
  } else if (!committed) {
    console.log('We aborted the transaction because ' + transactionAbortionCause);  //use the variable
  } else {
    console.log('User ada added!');
  }
  console.log("Ada's data: ", snapshot.val());
});
Run Code Online (Sandbox Code Playgroud)

如果我没记错的话,你也可以像在你的代码中那样使用 promise 来做到这一点。文档说交易返回一个非空的firebase.Promise包含{committed: boolean, snapshot: nullable firebase.database.DataSnapshot}并解释这个承诺“可以选择使用而不是onComplete回调来处理成功和失败”。

所以通过:

  1. 这样做return;对你的流产两种情况,和
  2. 读取committed布尔值

您应该能够通过执行以下操作来处理代码中的流产情况

.then((result)=>{
    if (result.commited) {... } else { /*abortion!*/}
}) 
Run Code Online (Sandbox Code Playgroud)

但是我还没有测试过这种方法