我遇到一个奇怪的错误。首先让我解释一下我的数据库结构。我将不同酒店的可预订床存储在数据库中。结构是这样的:
/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]
未定义。这对我来说是莫名其妙的,原因有两个:
hotelBedTimeouts
填充了每家酒店的列表,其中每个酒店都容纳1-30张床的空对象。hotelBedTimeouts[24]
因此无法进行未定义的评估。此错误的原因是什么,我该如何解决?
答案 0 :(得分:2)
这意味着,如果您的代码取决于特定的执行顺序,则需要确保其按该顺序执行。
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...
})
);