代码之家  ›  专栏  ›  技术社区  ›  Sahand

存储在firebase服务器中的数组有时将其元素设置为未定义

  •  1
  • Sahand  · 技术社区  · 6 年前

    我有个奇怪的错误。首先让我解释一下我的数据库结构。我在数据库中存储不同酒店的可预订床位。结构如下:

    /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张床的空物件。 热键超时[24] 因此,不可能对未定义进行评估。
    2. 同一张床可以单独预定和取消预定 就在错误之后 .

    这个错误的原因是什么?我该如何修复它?

    1 回复  |  直到 6 年前
        1
  •  2
  •   FatalMerlin    6 年前

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

    这将确保 计划自由床 只能在初始化完成后触发。

    这也意味着 更新 如果在初始化过程中更改了数据,将被忽略!

    由于上面的代码显然不起作用,因为异步导出注册显然是一个可怕的想法,下面的代码片段应该是另一种选择,除了确保它在正确初始化之后才执行之外,它还有一个额外的好处,那就是确保调度将按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...
        })
    );