主线程同步XMLHttpRequest已弃用,求async:false替代方案
同步AJAX报错的替代解决方案
问题背景
调用多个嵌套AJAX接口时,控制台抛出警告:
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects for the end user’s experience
确认问题源于代码中设置的async:false,但移除该参数后页面渲染顺序混乱,显示异常。
核心原因
async:false会强制AJAX同步执行,阻塞浏览器主线程,导致页面卡顿、无响应,这也是浏览器将其标记为废弃的原因。原代码依赖同步的执行顺序:先获取所有分类,再逐个分类获取商品,最后按顺序渲染DOM。移除async:false后,异步请求会并行执行,导致DOM渲染顺序错乱,所以页面异常。
替代方案
方案1:使用Promise + async/await(推荐,代码更清晰)
利用ES6的async/await让异步代码以同步逻辑编写,同时保持异步执行,不阻塞主线程。结合jQuery 3.0+版本AJAX返回Promise对象的特性,可轻松改造代码:
修改后的代码:
<script> // 封装获取分类的AJAX为Promise函数 async function getAllCategories() { return $.ajax({ method: "POST", url: "API/actions/getAllCategories" }); } // 封装获取分类商品的AJAX为Promise函数 async function getProductsByCatId(categoryId, countryId) { return $.ajax({ method: "POST", url: "API/actions/getProductsbycatId", data: { "categoryId": categoryId, "countryId": `<?php echo $_SESSION['evoucher']['countryId'] ?> ` } }); } // 主逻辑用async/await控制顺序 async function renderCategoryTabs() { try { const categories = await getAllCategories(); let newdiv = ""; let count = 1; for (const row of categories) { // 构建导航项 if (count === 1) { newdiv += ` <li class="active"> <a data-toggle="tab" href="#tab${count}" aria-expanded="true"> <span>${row.name}</span> </a> </li>`; } else { newdiv += ` <li> <a data-toggle="tab" href="#tab${count}" aria-expanded="true"> <span>${row.name}</span> </a> </li>`; } // 等待当前分类的商品请求完成 const products = await getProductsByCatId(row.id, `<?php echo $_SESSION['evoucher']['countryId'] ?> `); let active = count === 1 ? "active in" : ""; let newdiv1 = ` <div id="tab${count}" class="tab-pane fade ${active}"> <div class="bbb_viewed"> <div class="row"> <div class="col"> <div class="bbb_main_container"> <div class="bbb_viewed_title_container"> <div class="bbb_viewed_nav_container"> <div class="bbb_viewed_nav bbb_viewed_prev"><i class="fas fa-chevron-left"></i></div> <div class="bbb_viewed_nav bbb_viewed_next"><i class="fas fa-chevron-right"></i></div> </div> </div> <div class="bbb_viewed_slider_container"> <div class="owl-carousel owl-theme bbb_viewed_slider">`; products.forEach(row1 => { newdiv1 += ` <div class="owl-item"> <div class="bbb_viewed_item1 discount d-flex flex-column align-items-center justify-content-center text-center"> <div class="bbb_viewed_image"><img src="${row1.pathimage2}" alt=""></div> <div class="bbb_viewed_content text-center"> <div class="bbb_viewed_name"><a href="#">${row1.name}</a></div> </div> </div> </div>`; }); newdiv1 += ` </div> </div> </div> </div> </div> </div> </div>`; $("#tabs").append(newdiv1); count++; } $("#bestsellernav").html(newdiv); // 渲染完成后初始化轮播 $('.bbb_viewed_slider').owlCarousel(); } catch (error) { alert("加载数据失败"); console.error(error); } } // 执行主函数 renderCategoryTabs(); </script>
方案2:使用jQuery的Deferred对象(兼容旧版jQuery)
如果项目使用jQuery 3.0以下版本,可通过$.Deferred管理异步流程:
<script> function getAllCategories() { return $.ajax({ method: "POST", url: "API/actions/getAllCategories" }); } function getProductsByCatId(categoryId, countryId) { return $.ajax({ method: "POST", url: "API/actions/getProductsbycatId", data: { "categoryId": categoryId, "countryId": `<?php echo $_SESSION['evoucher']['countryId'] ?> ` } }); } getAllCategories().done(function(categories) { let newdiv = ""; let count = 1; // 用链式调用保证顺序 let sequence = $.Deferred().resolve(); categories.forEach(row => { sequence = sequence.then(function() { // 构建导航项 if (count === 1) { newdiv += ` <li class="active"> <a data-toggle="tab" href="#tab${count}" aria-expanded="true"> <span>${row.name}</span> </a> </li>`; } else { newdiv += ` <li> <a data-toggle="tab" href="#tab${count}" aria-expanded="true"> <span>${row.name}</span> </a> </li>`; } // 请求当前分类商品 return getProductsByCatId(row.id, `<?php echo $_SESSION['evoucher']['countryId'] ?> `).done(function(products) { let active = count === 1 ? "active in" : ""; let newdiv1 = ` <div id="tab${count}" class="tab-pane fade ${active}"> <div class="bbb_viewed"> <div class="row"> <div class="col"> <div class="bbb_main_container"> <div class="bbb_viewed_title_container"> <div class="bbb_viewed_nav_container"> <div class="bbb_viewed_nav bbb_viewed_prev"><i class="fas fa-chevron-left"></i></div> <div class="bbb_viewed_nav bbb_viewed_next"><i class="fas fa-chevron-right"></i></div> </div> </div> <div class="bbb_viewed_slider_container"> <div class="owl-carousel owl-theme bbb_viewed_slider">`; products.forEach(row1 => { newdiv1 += ` <div class="owl-item"> <div class="bbb_viewed_item1 discount d-flex flex-column align-items-center justify-content-center text-center"> <div class="bbb_viewed_image"><img src="${row1.pathimage2}" alt=""></div> <div class="bbb_viewed_content text-center"> <div class="bbb_viewed_name"><a href="#">${row1.name}</a></div> </div> </div> </div>`; }); newdiv1 += ` </div> </div> </div> </div> </div> </div>`; $("#tabs").append(newdiv1); count++; }); }); }); // 所有请求完成后更新导航 sequence.done(function() { $("#bestsellernav").html(newdiv); $('.bbb_viewed_slider').owlCarousel(); }); }).fail(function(error) { alert("加载数据失败"); console.error(error); }); </script>
额外优化建议
- 批量请求优化:后端新增接口一次性返回所有分类及对应商品数据,减少前端AJAX请求次数,提升性能。
- 加载状态提示:请求过程中添加加载动画,避免页面长时间空白,提升用户体验。
内容的提问来源于stack exchange,提问作者maria jansian
相关产品推荐
相关产品推荐

