嵌套getJSON调用后输出序列异常,求正确实现方案
问题分析与正确实现方案
嘿,我一眼就看出问题出在哪了——这是JavaScript里经典的闭包与异步执行顺序的坑!同时你的代码还有一个隐藏的逻辑漏洞,我来一步步给你拆解:
问题根源
- 变量作用域问题:你用
var声明的projectName和myURL是函数级作用域,整个循环里只有这一个变量的引用。当$.getJSON的异步回调触发时,循环早就跑完了,这两个变量已经被更新到最后一次循环的值(也就是"My Project"对应的那组),所以所有回调里拿到的都是最后一次的projectName。 - 异步等待不完整:外层的
$.when只等待了获取项目列表的请求,里面的多个详情请求并没有被纳入等待队列,所以done回调执行时,可能还有很多详情请求没完成,myOutput的数据也会不完整。
正确实现方式(现代ES6版本)
用let创建块级作用域,同时收集所有异步请求的Promise统一等待,代码如下:
var myOutput = []; // 先获取项目列表 $.getJSON(projectListURL) .then(function(data) { var compArr = data.components; // 把每个项目转换成详情请求的Promise,同时保留当前项目的名称 var detailPromises = compArr.map(function(comp) { let projectKey = comp.key; let projectName = comp.name; let myURL = "http://myserver.com/projectdetails?projectkey=" + projectKey; // 返回包含项目名称和详情的Promise return $.getJSON(myURL).then(function(detailData) { return { name: projectName, status: detailData.projectStatus.status }; }); }); // 等待所有详情请求完成 return $.when.apply($, detailPromises); }) .done(function(...results) { // results就是所有请求返回的对象数组,直接赋值给myOutput myOutput = results; console.log(myOutput); });
兼容旧环境的实现方式(IIFE版本)
如果你的项目需要兼容不支持ES6的旧环境,可以用立即执行函数(IIFE)创建独立作用域:
var myOutput = []; $.getJSON(projectListURL) .then(function(data) { var compArr = data.components; var detailPromises = []; for (var i = 0; i < compArr.length; i++) { // 用IIFE保存当前迭代的项目信息,避免变量被覆盖 (function(projectKey, projectName) { var myURL = "http://myserver.com/projectdetails?projectkey=" + projectKey; var promise = $.getJSON(myURL).then(function(detailData) { return { name: projectName, status: detailData.projectStatus.status }; }); detailPromises.push(promise); })(compArr[i].key, compArr[i].name); } return $.when.apply($, detailPromises); }) .done(function() { // 将arguments对象转换为数组 myOutput = Array.prototype.slice.call(arguments); console.log(myOutput); });
关键要点总结
- 避免在异步循环中使用
var,改用let或者IIFE来创建独立作用域,确保每个异步回调能拿到当前迭代的变量值。 - 必须收集所有异步请求的Promise,用
$.when.apply($, 数组)统一等待,才能保证所有请求完成后再处理最终结果。
内容的提问来源于stack exchange,提问作者reiley
相关产品推荐
相关产品推荐

