You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题根源

  1. 异步回调错误无法拦截:在_checkDuplicateChat的回调里throw error属于异步上下文抛出,无法被外部捕获,会直接变成未捕获异常,导致后续创建逻辑继续执行。
  2. 异步流程管控失效:原代码的嵌套回调逻辑本身没问题,但错误处理方式不对,且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
    }
}

额外优化建议

  1. 数据库查询性能优化:当前遍历所有聊天室的方式在数据量大时性能差,可给ChatRooms节点添加复合索引,或设计基于用户ID排序的唯一标识(比如将两个用户ID按字典序拼接成字符串),实现精准查询。
  2. 错误处理一致性:确保所有异步操作的错误都通过onComplete回调传递,避免出现未捕获异常。

内容的提问来源于stack exchange,提问作者Ahmet-Salman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 00:12:23