jQuery嵌套Ajax添加DataTable行时外层循环先完成迭代问题如何解决
问题根源
- 该执行顺序问题由JavaScript事件循环机制和Ajax的异步特性导致:同步执行的for循环会优先在主线程完成全部迭代,先将所有外层分类行添加到DataTable,内层异步Ajax请求会被推入任务队列,等待主线程空闲后才会依次执行,因此出现外层行全部加载完成后才处理内层请求的现象。
- 原代码使用
var声明循环变量,存在闭包陷阱:所有内层Ajax请求拿到的id、project_id都会是循环最后一次迭代的值,会导致请求参数错误。
修复方案
方案1:串行执行(保证行添加顺序与分类顺序完全一致)
用async/await改写异步逻辑,每次循环等待内层请求处理完成后再进入下一次迭代,同时优化DataTable渲染性能:
$.ajax({ url: getProjDocAddress, type: "POST", async: true, data: { project_id: project_id, _token: $('meta[name="csrf-token"]').attr("content") }, dataType: 'json', // 将success回调声明为异步函数 success: async function (result) { if(result['project_docs_section'] != null) { var len = result['project_docs_section'].length; var table = projectDocsDataTable.DataTable({ "paging": true, "lengthChange": false, "searching": true, "ordering": true, "info": true, "autoWidth": true, "sDom": 'lfrtip' }); var index=1; // 用let声明块级作用域循环变量,避免闭包问题 for(let i=0; i<len; i++) { let id = result['project_docs_section'][i].id; let section_title = result['project_docs_section'][i].section_title; let project_id = result['project_docs_section'][i].project_id; // 添加外层分类行 table.row.add( [ section_title+"<a class='manage-drawing-files-btn' data-value='"+id+"' data-id='0' data-path='' data-url='' data-type='docs' data-file-type='' data-is-docs='0' data-is-add='1'> <i class='fa fa-upload'></i></a>", '', '', '' ]); // await等待内层Ajax请求完成 await $.ajax({ url: url + "/" + id, type: "POST", async: true, data: { project_id: project_id, _token: $('meta[name="csrf-token"]').attr("content") }, dataType: 'json', success: function (response) { if(response['projectFiles'] != null) { let len1 = response['projectFiles'].length; for(let j=0; j<len1; j++) { table.row.add( [ index, response['projectFiles'][j].type, response['projectFiles'][j].file_type, '<center><a href="'+response['projectFiles'][j].file_url+'" download="'+response['projectFiles'][j].type+'"><i data-id="'+response['projectFiles'][j].id+'"class="edit-doc-btn fa fa-download text-primary icon-action"></i></a><a data-tkn="'+response['projectFiles'][j].token+'" data-id="'+response['projectFiles'][j].id+'" data-path="'+response['projectFiles'][j].file + response['projectFiles'][j].file_name +'" data-url="'+response['projectFiles'][j].file_url+'"data-desc="'+response['projectFiles'][j].description+'" data-type="'+response['projectFiles'][j].type+'" data-is-docs="1" data-file-type="'+response['projectFiles'][j].file_type+'" " class=" manage-drawing-files-btn edit-doc-btn feather icon-edit-2 text-info icon-action"></i></a><a data-id="'+response['projectFiles'][j].id+'" class="delete-doc-btn feather icon-trash-2 text-danger icon-action"></i></a></center>' ]); index++; } } } }); } // 所有行添加完成后统一渲染,提升性能 table.draw(false); } } });
方案2:并行执行(加载速度更快,适合分类数量较多的场景)
如果不需要严格保证分类行的添加顺序,可以将所有内层请求用Promise.all批量发起,等待所有请求返回后统一处理行添加,加载效率更高:
- 先收集所有内层Ajax请求的Promise实例
- 用
Promise.all等待所有请求返回结果 - 按顺序处理每个分类的外层行和对应文件行,最后统一渲染表格
内容的提问来源于stack exchange,提问作者Bilal Hameed
相关产品推荐
相关产品推荐

