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

循环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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:16:44