存储在Firebase服务器中的数组有时将其元素设置为undefined

时间:2018-07-25 07:20:30

标签: javascript firebase firebase-realtime-database

我遇到一个奇怪的错误。首先让我解释一下我的数据库结构。我将不同酒店的可预订床存储在数据库中。结构是这样的:

/beds
|
|- hotel1
|    |---- bed1
|    |
|    |---- bed2
|
|- hotel2
|    |---- bed1
|    |
|    |---- bed2
|
|- hotel3
     etc...

用户可以预订床,这样,在考虑是否要预订床时,没有其他人可以预订床。为此有一个5分钟的计时器。为了避免计时器过多,我在服务器中有一个列表,其中包含每个酒店的列表,而该列表又为酒店的每个床位都包含一个对象:

const hotelBedTimeouts = [];
var beds = db.ref('/beds');

// Initialise the bed timeout holder object
beds.once("value", function(snapshot){
  var hotels = snapshot.val();

  for (var i = 0; i < hotels.length; i++) {
    // push empty list to be filled with lists holding individual bed timeouts
    if(hotels[i]){
      hotelBedTimeouts.push([]);
      for(var j = 0; j < hotels[i].length; j++) {
        // this list will hold all timeouts for this bed
        hotelBedTimeouts[i].push({});
      }
    } else {
      hotelBedTimeouts.push(undefined);
    }
  }
});

这就是我创建空的计时器持有人结构的方式。然后,每当有Firebase功能的客户预订床铺时,我就设置一个计时器。我还使用该功能在用户退出页面时取消计时器:

// Frees a bed after a set amount of time
exports.scheduleFreeBed = functions.database.ref('/beds/{hotelIndex}/{bedIndex}/email').onUpdate( (snapshot, context) => {
  var originalEmail = snapshot.after.val();
  var hotelIndex = context.params.hotelIndex;
  var bedIndex = context.params.bedIndex;
  if (originalEmail === -1) {

    console.log("Cancelling timeout for chair number " + bedIndex + " with...");
    console.log("hotelIndex: " + hotelIndex);
    console.log("hotelBedTimeouts[hotelIndex]:");
    console.log(hotelBedTimeouts[hotelIndex]);
    console.log("hotelBedTimeouts[hotelIndex][bedIndex]");
    console.log(hotelBedTimeouts[hotelIndex][bedIndex]);

    clearTimeout(hotelBedTimeouts[hotelIndex][bedIndex].timeoutFunc); // clear current timeoutfunc
    return 0; // Do nothing
  }

  console.log("Setting timeout for bed number " + bedIndex + " with...");
  console.log("hotelIndex: " + hotelIndex);
  console.log("hotelBedTimeouts[hotelIndex]:");
  console.log(hotelBedTimeouts[hotelIndex]);
  console.log("hotelBedTimeouts[hotelIndex][bedIndex]");
  console.log(hotelBedTimeouts[hotelIndex][bedIndex]);

  // replace old timeout function
  hotelBedTimeouts[hotelIndex][bedIndex].timeoutFunc = setTimeout(function () {
    var bedRef = admin.database().ref(`/beds/${hotelIndex}/${bedIndex}`);
    bedRef.once("value", function(bedSnap){
      var bed = bedSnap.val();
      var booked = bed.booked;
      if (!booked) {
        var currentEmail = bed.email;
        // Check if current bed/email is the same as originalEmail
        if (currentEmail === originalEmail) {
          bedSnap.child("email").ref.set(-1, function() {
            console.log("Freed bed");
          });
        }
      }
    });
  }, 300000); // 5 min timeout

  return 0;
});

这在大多数情况下都可以正常工作。但是,如果我同时预订许多床,则某些椅子会出错。这是错误的样子:

Cancelling timeout for bed number 24 with...    

hotelIndex: 1

hotelBedTimeouts[hotelIndex]:

undefined

hotelBedTimeouts[hotelIndex][bedIndex]

TypeError: Cannot read property '24' of undefined
    at exports.scheduleFreeBed.functions.database.ref.onUpdate (/user_code/index.js:698:50)
    at Object.<anonymous> (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:112:27)
    at next (native)
    at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:28:71
    at __awaiter (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:24:12)
    at cloudFunction (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:82:36)
    at /var/tmp/worker/worker.js:728:24
    at process._tickDomainCallback (internal/process/next_tick.js:135:7)

似乎hotelBedTimeouts[24]未定义。这对我来说是莫名其妙的,原因有两个:

  1. 我已经为hotelBedTimeouts填充了每家酒店的列表,其中每个酒店都容纳1-30张床的空对象。hotelBedTimeouts[24]因此无法进行未定义的评估。
  2. 在错误发生后立即在同一张床上单独进行预定和“取消”预定都可以。

此错误的原因是什么,我该如何解决?

1 个答案:

答案 0 :(得分:2)

Firebase高度异步

这意味着,如果您的代码取决于特定的执行顺序,则需要确保其按该顺序执行。

once函数返回一个Promise(有关承诺here的更多信息)。您可以在scheduleFreeBed回调函数中注册Promise.then()函数,以便在初始化完成后注册onUpdate

例如:

// Initialise the bed timeout holder object
beds.once("value", function (snapshot) {
    // your existing code...
}).then(() => {
    // Frees a bed after a set amount of time
    exports.scheduleFreeBed = functions.database.ref('/beds/{hotelIndex}/{bedIndex}/email').onUpdate( (snapshot, context) => {
        // your existing code...
    });
})

这将确保scheduleFreeBed仅在初始化完成后才能被触发。

这也意味着如果在初始化期间更改数据,onUpdate将被忽略!

由于上述方法显然不起作用,因为显然异步导出注册是一个可怕的想法,因此以下代码段应作为替代方案,其附加好处是除了确保调度按FIFO顺序外,还可以确保按FIFO顺序进行调度仅在正确初始化后执行。通过此更改,还将避免忽略初始化期间触发的先前缺点:

// Initialize the bed timeout holder object
var initPromise = beds.once("value", function (snapshot) {
    // your existing code...
});

// Frees a bed after a set amount of time
exports.scheduleFreeBed = functions.database.ref('/beds/{hotelIndex}/{bedIndex}/email').onUpdate( (snapshot, context) =>
    // make sure the scheduling happens after the initialization and in order
    // since his chaining doubles as a queue
    initPromise = initPromise.then(() => {
    // your existing code...
    })
);