添加.done/.fail回调后jQuery each循环中key值不再递增的问题排查
问题原因:异步回调与闭包作用域的坑
先理清楚你遇到的问题:看起来循环里的key好像不再递增,但本质是所有异步回调执行时,都拿到了循环最后一次的key和selector值,这是JavaScript异步操作结合闭包作用域的经典问题,我来给你拆解清楚:
为什么会出现这个问题?
$.each是同步循环,会一口气把data里的所有项遍历完;但$.get是异步HTTP请求——也就是说,循环在几毫秒内就跑完了,而请求的.done()/.fail()回调要等服务器响应后才会执行。
循环里的selector和key是在外部作用域定义的变量,所有的异步回调都会共享这个变量的引用。当回调触发的时候,整个循环已经执行完毕,key已经变成了循环的最后一个值(比如data有4条数据的话,key就是3),selector也固定成了#img4,所以所有回调都只会操作最后一个图片容器,看起来就像“key不再递增”。
怎么解决?
给你三种常用的解决方案,按需选择:
方案1:用let声明变量(ES6+推荐)
把循环内的selector、img这些变量用let声明,利用let的块级作用域特性,每次循环都会创建一个独立的作用域,回调会绑定当前迭代的变量值:
$.each(data, function(key, val) { let img = '/wp-content/img/history/' + val.image_id + '.jpg'; let selector = '#img' + (key + 1); $(selector).removeClass('d-none'); $.get(img).done(function() { $(selector).empty(); // 这里注意你原来的代码多了个无值的id属性,建议去掉或补全 $(selector).prepend('<img src="' + img + '" alt="pic" />'); }).fail(function() { alert(val.image_id + ' not found'); }); });
方案2:自执行函数(IIFE)兼容ES5
如果需要兼容旧浏览器,可以用立即执行函数把当前循环的变量包裹起来,创建独立作用域:
$.each(data, function(key, val) { var img = '/wp-content/img/history/' + val.image_id + '.jpg'; var selector = '#img' + (key + 1); $(selector).removeClass('d-none'); // 用IIFE保存当前迭代的变量 (function(currentSel, currentImg, currentVal) { $.get(currentImg).done(function() { $(currentSel).empty(); $(currentSel).prepend('<img src="' + currentImg + '" alt="pic" />'); }).fail(function() { alert(currentVal.image_id + ' not found'); }); })(selector, img, val); });
方案3:改用forEach遍历
如果data是数组的话,直接用Array.prototype.forEach也是个好选择——它的回调本身就会形成独立作用域,不用额外处理:
data.forEach(function(val, index) { let img = '/wp-content/img/history/' + val.image_id + '.jpg'; let selector = '#img' + (index + 1); $(selector).removeClass('d-none'); $.get(img).done(function() { $(selector).empty(); $(selector).prepend('<img src="' + img + '" alt="pic" />'); }).fail(function() { alert(val.image_id + ' not found'); }); });
内容的提问来源于stack exchange,提问作者Mark Ballard
相关产品推荐
相关产品推荐

