循环GeoJSON特征时AJAX数据未更新的原因排查求助
问题原因分析
这是典型的异步请求与循环变量作用域冲突的问题。你用var声明的updatedLayerGeojsonId、updatedLayerGeojsonTable、updatedLayerGeojsonGeometry是函数级作用域变量,而AJAX请求是异步执行的——循环会在瞬间跑完,但AJAX的success回调要等服务器响应才会触发,这时候这些变量早就被最后一轮循环的值覆盖了,所以所有后续执行的AJAX请求都会用最后一次循环的变量值。
解决方案
我给你三种可行的修复方式,按推荐程度排序:
1. 使用let代替var声明变量(ES6+最优方案)
let是块级作用域,每一轮循环都会创建新的变量绑定,异步回调能精准捕获当前循环的变量值,代码改动最小:
var geojsonFeatures = geojson.features; var ajaxDeferred = []; // 循环变量和内部变量都用let声明 for(let a = 0; a < geojsonFeatures.length; a++){ let updatedLayerGeojson = geojsonFeatures[a].geometry; updatedLayerGeojson.crs = { "type": "name", "properties": { "name": "epsg:4326" } }; let updatedLayerGeojsonGeometry = JSON.stringify(updatedLayerGeojson); let updatedLayerGeojsonId = geojsonFeatures[a].properties.gid; let updatedLayerGeojsonTable = geojsonFeatures[a].properties.layer_table; ajaxDeferred.push( $.ajax({ url: window.location.origin + '/csrfToken', success: function(response) { $.ajax({ url: '/maplayers/saveEditedLayerRecord', type:"post", data: { id: updatedLayerGeojsonId, table: updatedLayerGeojsonTable, geom: updatedLayerGeojsonGeometry }, beforeSend: function(xhr, settings){ xhr.setRequestHeader('X-CSRF-Token', response._csrf); }, success: function(data){ if(data){ numberOfEditedLayersCompleted++; if(numberOfEditedLayers == numberOfEditedLayersCompleted){ removeLayers(); editableLayers.clearLayers(); editedLayer = false; numberOfEditedLayers = 0; numberOfEditedLayersCompleted = 0; } } }, cache: false }); } }) ); }
2. 使用forEach遍历(更简洁的写法)
forEach的回调函数自带独立作用域,每一次遍历都会创建新的变量绑定,也能避开作用域冲突问题:
var geojsonFeatures = geojson.features; var ajaxDeferred = []; geojsonFeatures.forEach(function(feature) { let updatedLayerGeojson = feature.geometry; updatedLayerGeojson.crs = { "type": "name", "properties": { "name": "epsg:4326" } }; let updatedLayerGeojsonGeometry = JSON.stringify(updatedLayerGeojson); let updatedLayerGeojsonId = feature.properties.gid; let updatedLayerGeojsonTable = feature.properties.layer_table; ajaxDeferred.push( $.ajax({ url: window.location.origin + '/csrfToken', success: function(response) { $.ajax({ url: '/maplayers/saveEditedLayerRecord', type:"post", data: { id: updatedLayerGeojsonId, table: updatedLayerGeojsonTable, geom: updatedLayerGeojsonGeometry }, beforeSend: function(xhr, settings){ xhr.setRequestHeader('X-CSRF-Token', response._csrf); }, success: function(data){ if(data){ numberOfEditedLayersCompleted++; if(numberOfEditedLayers == numberOfEditedLayersCompleted){ removeLayers(); editableLayers.clearLayers(); editedLayer = false; numberOfEditedLayers = 0; numberOfEditedLayersCompleted = 0; } } }, cache: false }); } }) ); });
3. 立即执行函数(IIFE)兼容旧环境
如果项目需要兼容不支持ES6的环境,用立即执行函数把每轮循环的变量封装成独立作用域:
var geojsonFeatures = geojson.features; var ajaxDeferred = []; for(var a = 0; a < geojsonFeatures.length; a++){ // 用IIFE封装当前循环的变量 (function(currentId, currentTable, currentGeom) { ajaxDeferred.push( $.ajax({ url: window.location.origin + '/csrfToken', success: function(response) { $.ajax({ url: '/maplayers/saveEditedLayerRecord', type:"post", data: { id: currentId, table: currentTable, geom: currentGeom }, beforeSend: function(xhr, settings){ xhr.setRequestHeader('X-CSRF-Token', response._csrf); }, success: function(data){ if(data){ numberOfEditedLayersCompleted++; if(numberOfEditedLayers == numberOfEditedLayersCompleted){ removeLayers(); editableLayers.clearLayers(); editedLayer = false; numberOfEditedLayers = 0; numberOfEditedLayersCompleted = 0; } } }, cache: false }); } }) ); })(geojsonFeatures[a].properties.gid, geojsonFeatures[a].properties.layer_table, (function(){ let geom = geojsonFeatures[a].geometry; geom.crs = { "type": "name", "properties": { "name": "epsg:4326" } }; return JSON.stringify(geom); })()); }
额外优化建议
你现在每次请求都先获取CSRF Token,其实可以一次性获取Token后再发起所有保存请求,减少冗余请求:
// 先统一获取CSRF Token $.ajax({ url: window.location.origin + '/csrfToken', success: function(response) { const csrfToken = response._csrf; const geojsonFeatures = geojson.features; const ajaxDeferred = []; geojsonFeatures.forEach(function(feature) { let updatedLayerGeojson = feature.geometry; updatedLayerGeojson.crs = { "type": "name", "properties": { "name": "epsg:4326" } }; let updatedLayerGeojsonGeometry = JSON.stringify(updatedLayerGeojson); let updatedLayerGeojsonId = feature.properties.gid; let updatedLayerGeojsonTable = feature.properties.layer_table; ajaxDeferred.push( $.ajax({ url: '/maplayers/saveEditedLayerRecord', type:"post", data: { id: updatedLayerGeojsonId, table: updatedLayerGeojsonTable, geom: updatedLayerGeojsonGeometry }, beforeSend: function(xhr, settings){ xhr.setRequestHeader('X-CSRF-Token', csrfToken); }, success: function(data){ if(data) numberOfEditedLayersCompleted++; }, cache: false }) ); }); // 用$.when统一处理所有请求完成事件 $.when(...ajaxDeferred).done(function() { removeLayers(); editableLayers.clearLayers(); editedLayer = false; numberOfEditedLayers = 0; numberOfEditedLayersCompleted = 0; }); } });
内容的提问来源于stack exchange,提问作者mblais29
相关产品推荐
相关产品推荐

