Firebase与NodeJS中语句执行顺序异常问题求助
聊天室重复检测异步执行异常问题
我定义了ChatRoom类的构造函数及核心方法,负责创建聊天室并写入Firebase实时数据库,但_checkDuplicateChat方法的异步查询逻辑存在问题——重复检测还没完成,创建聊天室的代码就已经执行了。我试过用async/await和嵌套回调,但都没解决。
ChatRoom构造函数
class ChatRoom { constructor(title, userA, userB, advertId, onComplete, fromRef) { this.members = [{ userId: userA.userId, username: userA.username || '', photo: userA.photo || '', }, { userId: userB.userId, username: userB.username || '', photo: userB.photo || '' }]; this.createdAt = Date.now() this.advertId = advertId this.isRemoved = false; if (_.isEmpty(fromRef)) { this._sendTofirebase(onComplete) } else { this.chatRoomRef = fromRef } }
_sendTofirebase核心方法
_sendTofirebase(onComplete) { this._checkDuplicateChat((isDuplicate) => { if (isDuplicate) { var error = new ChatRoomError("Chat room was not created") error.code = '409_DUPLICATE_RECORD_FOUND' throw error } // create chat room reference in firebase this.chatRoomRef = firebase().ref('ChatRooms/').push({ title: this.title, advertId: this.advertId, members: this.members, createdAt: this.createdAt, isRemoved: this.isRemoved, }, onComplete); var chatKey = this.chatRoomRef.key // create user chat reference in firebase this.UserChatRef = firebase().ref("UsersChat").child(this.members[0].userId).child(chatKey).set({"lastModified":Date.now(), "lastMessage" : ''}, (err) => { if (err) { throw new ChatRoomError(err) } }); // create user chat reference in firebase this.UserChatRef = firebase().ref("UsersChat").child(this.members[1].userId).child(chatKey).set({"lastModified":Date.now(), "lastMessage" : ''}, (err) => { if (err) { throw new ChatRoomError(err) } }); }); }
_checkDuplicateChat检测方法
_checkDuplicateChat(onComplete) { const userA_ID = this.members[0].userId const userB_ID = this.members[1].userId this.chatRoomRef = firebase().ref('ChatRooms') var foundRoom = false this.chatRoomRef.once('value', (chatSnapshot) => { chatSnapshot.forEach((snapshot) => { var snap = snapshot.val() if ((snap.members[0].userId == userA_ID && snap.members[1].userId == userB_ID) || (snap.members[0].userId == userB_ID && snap.members[1].userId == userA_ID)) { foundRoom = true; } }) onComplete(foundRoom); }) }
问题根源
- 异步回调错误无法拦截:在
_checkDuplicateChat的回调里throw error属于异步上下文抛出,无法被外部捕获,会直接变成未捕获异常,导致后续创建逻辑继续执行。 - 异步流程管控失效:原代码的嵌套回调逻辑本身没问题,但错误处理方式不对,且Firebase的
set操作也是异步的,没有处理串行执行和错误传递。
修复方案
1. 将_checkDuplicateChat改为Promise形式
把回调式异步逻辑改为返回Promise,方便用async/await管控流程:
_checkDuplicateChat() { return new Promise((resolve) => { const userA_ID = this.members[0].userId const userB_ID = this.members[1].userId const chatRoomRef = firebase().ref('ChatRooms') chatRoomRef.once('value', (chatSnapshot) => { let foundRoom = false chatSnapshot.forEach((snapshot) => { const snap = snapshot.val() if ((snap.members[0].userId === userA_ID && snap.members[1].userId === userB_ID) || (snap.members[0].userId === userB_ID && snap.members[1].userId === userA_ID)) { foundRoom = true; // 找到重复后立即终止遍历,提升性能 return true; } }) resolve(foundRoom); }) }) }
2. 改造_sendTofirebase为async方法
用await控制异步流程,统一处理错误并传递给回调:
async _sendTofirebase(onComplete) { try { const isDuplicate = await this._checkDuplicateChat() if (isDuplicate) { const error = new ChatRoomError("Chat room was not created") error.code = '409_DUPLICATE_RECORD_FOUND' throw error } // 创建聊天室并获取唯一key const chatRoomRef = firebase().ref('ChatRooms/').push({ title: this.title, advertId: this.advertId, members: this.members, createdAt: this.createdAt, isRemoved: this.isRemoved, }) this.chatRoomRef = chatRoomRef const chatKey = chatRoomRef.key // 并行写入两个用户的关联记录,等待全部完成 await Promise.all([ firebase().ref("UsersChat").child(this.members[0].userId).child(chatKey).set({ "lastModified": Date.now(), "lastMessage": '' }), firebase().ref("UsersChat").child(this.members[1].userId).child(chatKey).set({ "lastModified": Date.now(), "lastMessage": '' }) ]) // 执行完成回调 if (typeof onComplete === 'function') { onComplete(null) } } catch (err) { // 捕获所有错误并传递给回调 if (typeof onComplete === 'function') { onComplete(err) } else { throw err } } }
3. 补全构造函数缺失的属性并处理异步调用
原构造函数未给this.title赋值,会导致创建聊天室时title字段异常,需补上;同时处理_sendTofirebase返回的Promise:
constructor(title, userA, userB, advertId, onComplete, fromRef) { this.title = title; // 补全缺失的title属性 this.members = [{ userId: userA.userId, username: userA.username || '', photo: userA.photo || '', }, { userId: userB.userId, username: userB.username || '', photo: userB.photo || '' }]; this.createdAt = Date.now() this.advertId = advertId this.isRemoved = false; if (_.isEmpty(fromRef)) { this._sendTofirebase(onComplete).catch(err => { // 兜底错误处理,避免未捕获异常 console.error('创建聊天室失败:', err) }) } else { this.chatRoomRef = fromRef } }
额外优化建议
- 数据库查询性能优化:当前遍历所有聊天室的方式在数据量大时性能差,可给
ChatRooms节点添加复合索引,或设计基于用户ID排序的唯一标识(比如将两个用户ID按字典序拼接成字符串),实现精准查询。 - 错误处理一致性:确保所有异步操作的错误都通过
onComplete回调传递,避免出现未捕获异常。
内容的提问来源于stack exchange,提问作者Ahmet-Salman
相关产品推荐
相关产品推荐

