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

JavaScript中回调与Promise失效:Firebase异步数据加载异常

解决Firebase异步加载导致populateUsers()提前执行的问题

我明白你遇到的异步加载坑了——Firebase的事件回调都是异步触发的,嵌套的child_added和value事件很容易让代码执行顺序乱掉,之前的Promise和回调没生效,大概率是因为没把所有异步操作统一管理起来。咱们一步步来修复:

先理清核心问题

  1. 你现在的写法是点击按钮后,先执行set(true),然后立刻监听child_added——但child_added会触发所有已存在的子节点+新增的子节点,每次触发又会发起一个异步的value请求,这些请求的完成时间不确定,导致populateUsers可能在数据还没返回时就被调用(或者你希望一次性加载完所有用户再渲染)。
  2. 你的populateUsers函数参数是单个用户对象,但代码里用了chattedWith.length,这明显是逻辑矛盾,得先修正这个。

方案1:只处理刚新增的聊天对象(最贴合你点击按钮的场景)

如果你只是想在点击添加按钮后,把刚新增的那个聊天用户渲染出来,完全不需要监听child_added,直接在set完成后获取对应用户数据就行:

$("body").on("click", ".si-add", function(e) {
    var _CU = $.cookie("_cu"), 
        _MT = $(this).parent().attr("data-id");

    // 先完成聊天关系的设置,等待操作完成
    _firebaseChatRef.child(_CU).child(_MT).set(true)
    .then(function() {
        // 设置成功后,一次性获取该用户的auth数据(用once代替on,避免实时监听)
        return _firebaseAuthRef.child(_MT).once("value");
    })
    .then(function(userSnapshot) {
        // 数据返回后再调用渲染函数
        populateUsers({
            name: userSnapshot.val().n,
            id: userSnapshot.key
        });
    })
    .catch(function(error) {
        console.error("加载用户数据失败:", error);
    });

    function populateUsers(chattedWith) {
        let _el = document.getElementsByClassName("chat-ind-body-wrap")[0];
        // 修正原代码中错误的length引用,直接渲染单个用户
        _el.insertAdjacentHTML('afterbegin', `
            <li class="chat-ind-user active-user" data-id="${chattedWith.id}">
                <div class="chat-ind-user-img">
                    <img src="assets/1.jpg">
                </div>
                <div class="chat-ind-user-data">
                    <div class="ciu-name"> ${chattedWith.name} </div>
                    <div class="ciu-text"> Hi, I am the text here ! </div>
                    <span class="ciu-time"> 2h ago </span>
                </div>
                <span class="user-availble">
                    <i class="fa fa-circle"></i>
                </span>
            </li>
        `);
    }
});

方案2:加载所有已存在的聊天对象

如果你需要把当前用户所有的聊天对象都渲染出来,就得把所有用户数据的异步请求包装成Promise,用Promise.all等待全部加载完成后再渲染:

$("body").on("click", ".si-add", function(e) {
    var _CU = $.cookie("_cu"), 
        _MT = $(this).parent().attr("data-id");

    // 先完成新增聊天节点的操作
    _firebaseChatRef.child(_CU).child(_MT).set(true)
    .then(function() {
        // 获取当前用户所有的聊天关系节点
        return _firebaseChatRef.child(_CU).once("value");
    })
    .then(function(chatSnapshot) {
        // 遍历所有聊天节点,生成获取用户数据的Promise数组
        const userPromises = [];
        chatSnapshot.forEach(function(childSnapshot) {
            const userId = childSnapshot.key;
            userPromises.push(_firebaseAuthRef.child(userId).once("value"));
        });
        // 等待所有用户数据加载完成
        return Promise.all(userPromises);
    })
    .then(function(userSnapshots) {
        // 把所有用户快照转换成需要的对象格式
        const users = userSnapshots.map(function(snap) {
            return {name: snap.val().n, id: snap.key};
        });
        // 调用渲染函数,此时所有数据都已加载完成
        populateUsers(users);
    })
    .catch(function(error) {
        console.error("加载数据失败:", error);
    });

    function populateUsers(chattedWithList) {
        let _el = document.getElementsByClassName("chat-ind-body-wrap")[0];
        // 遍历用户数组,逐个渲染DOM元素
        chattedWithList.forEach(function(chattedWith) {
            _el.insertAdjacentHTML('afterbegin', `
                <li class="chat-ind-user active-user" data-id="${chattedWith.id}">
                    <div class="chat-ind-user-img">
                        <img src="assets/1.jpg">
                    </div>
                    <div class="chat-ind-user-data">
                        <div class="ciu-name"> ${chattedWith.name} </div>
                        <div class="ciu-text"> Hi, I am the text here ! </div>
                        <span class="ciu-time"> 2h ago </span>
                    </div>
                    <span class="user-availble">
                        <i class="fa fa-circle"></i>
                    </span>
                </li>
            `);
        });
    }
});

为什么之前的方法没生效?

  1. 你之前的set(true).then()里是空的,没有把后续的用户数据请求纳入Promise链,导致异步操作还是各自为政。
  2. 用on("child_added")会实时监听节点变化,包括之前已存在的子节点,每次触发都会发起新的异步请求,你没有等待这些请求全部完成就可能执行了后续逻辑。
  3. 尽量用once("value")代替on("value"),除非你需要实时监听数据变化——一次性获取数据更高效,还能避免不必要的内存泄漏。

内容的提问来源于stack exchange,提问作者Shehny Khan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:48:16