JavaScript循环内使用fetch API的作用域问题及解决求助
解决循环中使用fetch API的作用域问题
嘿,我来帮你搞定这个问题!你遇到的这个提示是因为var声明的变量带来的作用域陷阱,加上fetch是异步操作,导致回调执行时拿到的不是你期望的迭代值。我来一步步给你解释并修改代码:
问题根源
你用var i来声明循环变量,var是函数级作用域,整个循环里只有一个i变量。当fetch的异步回调(then里的函数)执行时,循环早就跑完了,i的值已经变成了obj.length(也就是7),这时候obj[i]就是undefined,不仅会导致数据错误,还会触发那个lint警告——循环内的函数引用了外部作用域的共享变量。
另外,你的代码里push的是value:"ERROR",但预期输出里的键是status,这个也要修正哦。
方法1:用let代替var声明循环变量
let是块级作用域,每次循环迭代都会创建一个新的i变量,回调里拿到的就是当前迭代的那个i值,完美解决作用域问题:
const obj = [ {"Id":"10101","descr":"server1.com"}, {"Id":"10102","descr":"server2.com"}, {"Id":"10103","descr":"server3.com"}, {"Id":"10104","descr":"server4.com"}, {"Id":"10105","descr":"server5.com"}, {"Id":"10106","descr":"server6.com"}, {"Id":"10107","descr":"server7.com"} ]; const temp = []; // 把var改成let,让i变成块级作用域变量 for (let i = 0; i < obj.length; i++){ // 提前把当前项存下来,更稳妥 const currentItem = obj[i]; const id = currentItem.Id; fetch(`https://abced.com/api/${id}/value`, { method : "GET", headers: { "Authorization": "xyz" } }) .then(res => res.json()) .then(data => { const stats = data.status; if (stats !== "OK") { // 修正键名为status,匹配预期输出 temp.push({ Id: currentItem.Id, descr: currentItem.descr, status:"ERROR" }); } console.log(temp); }) .catch(x => console.log("fail:", x)) }
方法2:用forEach遍历数组
forEach的回调函数本身就有独立的作用域,每次迭代都会传入当前的数组项,完全避开作用域问题,代码也更简洁:
const obj = [ {"Id":"10101","descr":"server1.com"}, {"Id":"10102","descr":"server2.com"}, {"Id":"10103","descr":"server3.com"}, {"Id":"10104","descr":"server4.com"}, {"Id":"10105","descr":"server5.com"}, {"Id":"10106","descr":"server6.com"}, {"Id":"10107","descr":"server7.com"} ]; const temp = []; obj.forEach(currentItem => { const id = currentItem.Id; fetch(`https://abced.com/api/${id}/value`, { method : "GET", headers: { "Authorization": "xyz" } }) .then(res => res.json()) .then(data => { const stats = data.status; if (stats !== "OK") { temp.push({ Id: currentItem.Id, descr: currentItem.descr, status:"ERROR" }); } console.log(temp); }) .catch(x => console.log("fail:", x)) });
方法3:用async/await + Promise.all(推荐)
如果希望所有请求完成后再统一得到结果(而不是每次回调都打印),用async/await结合Promise.all是更优雅的方式,还能保证结果的完整性:
const obj = [ {"Id":"10101","descr":"server1.com"}, {"Id":"10102","descr":"server2.com"}, {"Id":"10103","descr":"server3.com"}, {"Id":"10104","descr":"server4.com"}, {"Id":"10105","descr":"server5.com"}, {"Id":"10106","descr":"server6.com"}, {"Id":"10107","descr":"server7.com"} ]; async function fetchAllServersStatus() { // 为每个服务器创建一个请求Promise const requestPromises = obj.map(currentItem => { const id = currentItem.Id; return fetch(`https://abced.com/api/${id}/value`, { method : "GET", headers: { "Authorization": "xyz" } }) .then(res => res.json()) .then(data => { // 只收集status不是OK的项 if (data.status !== "OK") { return { Id: currentItem.Id, descr: currentItem.descr, status:"ERROR" }; } return null; // status为OK的返回null,后续过滤掉 }) .catch(err => { console.log("请求失败:", err); // 请求失败也标记为ERROR return { Id: currentItem.Id, descr: currentItem.descr, status:"ERROR" }; }); }); // 等待所有请求完成 const allResults = await Promise.all(requestPromises); // 过滤掉null项,得到最终结果 const temp = allResults.filter(item => item !== null); console.log(temp); // 这里就是你想要的预期输出! } // 调用函数 fetchAllServersStatus();
这个方法的好处是,你能确保所有请求都完成后再处理结果,而且代码逻辑更清晰,像同步代码一样易读。
内容的提问来源于stack exchange,提问作者user3046893
相关产品推荐
相关产品推荐

