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

按正确顺序执行JavaScript的Ajax与非Ajax调用问题

解决异步AJAX与同步HTML追加混合场景下的通知顺序问题

这个问题我之前也碰到过,核心痛点就是异步请求的不可控性打乱了原本的同步渲染顺序,给你个适配这种混合场景的解决方案:

问题回顾

我现在有一段循环调用createAndAppendNotification函数的代码,这个函数会根据fromUserId是否存在,要么发起AJAX请求获取用户信息后生成通知HTML,要么直接生成HTML追加到页面。但因为AJAX是异步操作,那些需要AJAX的通知会比直接生成的晚出现在页面上,导致顺序混乱——比如本该排在首位的最新通知,如果需要AJAX,反而会被加到列表最后。Stack Overflow上的方案大多针对全AJAX的场景,不太适配我这种混合情况,求解决办法。

核心思路

因为异步请求的完成时间没法保证,我们不能等请求完成再往页面加元素,而是要先固定所有通知的顺序位置,再把内容填充进去。这样不管AJAX快慢,每个通知的位置一开始就确定好了,绝对不会乱序。

具体实现步骤

  • 第一步:循环的时候,先为每个通知创建一个空的占位容器,按顺序追加到目标元素里,同时把这个容器和对应的通知数据绑定。
  • 第二步:修改原函数,不再直接追加HTML到页面,而是把生成好的HTML填充到预先创建的占位容器中——不管是同步生成还是AJAX请求回来后生成,都只替换对应位置的内容。

修改后的完整代码

var index = 0; 
for (index = 0; index < currentAmountOfUnreadNotifications; ++index) { 
    var currNotification = response.notifications[index]; 
    // 先创建占位容器,按循环顺序追加,确保位置正确
    var placeholder = $('<div class="notification-placeholder"></div>');
    notificationHtmlElement.append(placeholder);
    // 调用修改后的函数,传入占位容器作为目标
    createAndFillNotification(placeholder, baseUrl, localBaseUrl, currNotification); 
} 

function createAndFillNotification(placeholderEle, baseUrl, localBaseUrl, notification) { 
    var notificationId = notification.notification_id; 
    var subjectLangKey = notification.subject_js_lang_key; 
    var messageLangKey = notification.message_js_lang_key; 
    var urlLangKey = notification.cta_url_js_lang_key; 
    var urlTranslation = res[urlLangKey]; 
    var fullCtaUrl = localBaseUrl ? `${localBaseUrl}/${urlTranslation}` : `${baseUrl}/${urlTranslation}`;
    
    var fromUserId = notification.from_user_id; 
    var fromUserAuthLevel = notification.from_user_auth_level; 
    var currLang = $('html').attr('lang'); 
    moment.locale(currLang); 
    var timeSince = moment.utc(notification.timestamp_inserted_utc).fromNow(); 

    // 需要AJAX获取用户信息的情况
    if (fromUserId) { 
        $.ajax({ 
            type: 'post', 
            cache: false, 
            url: `${baseUrl}/notification/ajaxmethods/getUserInfoForNotification`, 
            data: { 
                'user_id': fromUserId, 
                'auth_level': fromUserAuthLevel 
            }, 
            dataType: 'json', 
            success: function(response){ 
                var subjectTranslation = res[subjectLangKey].format(response.name); 
                var messageTranslation = res[messageLangKey].format(response.name); 
                var notificationHtml = `
                    <a class="notification-cta" href="${fullCtaUrl}" data-notification-id="${notificationId}">
                        <div class="media single-notification">
                            <div class="media-left align-self-center">
                                <span class="avatar avatar-online"><img src="${response.picture}" alt="avatar"></span>
                            </div>
                            <div class="media-body">
                                <h6 class="media-heading yellow darken-3">${subjectTranslation}</h6>
                                <p class="notification-text font-small-3 text-muted">${messageTranslation}</p>
                                <small class="float-left"><time class="media-meta text-muted">${timeSince}</time></small>
                                <small class="float-right">
                                    <span class="media-meta cursor-pointer blue text-muted notification-mark-read" data-notification-id="${notificationId}">${res.MarkAsRead}</span>
                                </small>
                            </div>
                        </div>
                    </a>
                `; 
                // 填充到预先创建的占位容器,位置不变
                placeholderEle.html(notificationHtml); 
            } 
        }); 
    } else { 
        // 不需要AJAX,直接生成HTML
        var subjectTranslation = res[subjectLangKey]; 
        var messageTranslation = res[messageLangKey]; 
        var iconClass = ""; 
        switch(notification.type) { 
            case '0': iconClass = "ft-info"; break; 
            case '1': iconClass = "ft-check-circle"; break; 
            case '2': iconClass = "ft-info"; break; 
            case '3': iconClass = "ft-alert-triangle"; break; 
            case '4': iconClass = "ft-x-circle"; break; 
        } 
        var notificationHtml = `
            <a class="notification-cta" href="${fullCtaUrl}" data-notification-id="${notificationId}">
                <div class="media single-notification">
                    <div class="media-left align-self-center">
                        <i class="${iconClass} icon-bg-circle bg-yellow bg-darken-3"></i>
                    </div>
                    <div class="media-body">
                        <h6 class="media-heading yellow darken-3">${subjectTranslation}</h6>
                        <p class="notification-text font-small-3 text-muted">${messageTranslation}</p>
                        <small class="float-left"><time class="media-meta text-muted">${timeSince}</time></small>
                        <small class="float-right">
                            <span class="media-meta cursor-pointer blue text-muted notification-mark-read" data-notification-id="${notificationId}">${res.MarkAsRead}</span>
                        </small>
                    </div>
                </div>
            </a>
        `; 
        // 直接填充到占位容器
        placeholderEle.html(notificationHtml); 
    } 
}

额外优化建议

  • 可以给占位容器加个简单的加载状态,比如<div class="notification-placeholder">加载中...</div>或者加个CSS动画,这样用户能看到通知正在加载,体验更好。
  • 如果AJAX请求失败,记得加个error回调,给占位容器填充错误提示或者默认内容,避免一直空白。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:09:51