按正确顺序执行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
相关产品推荐
相关产品推荐

