FCC Twitch API项目:for循环内的if-else无法遍历所有情况
你遇到的两个问题都是异步回调中的变量作用域陷阱导致的——简单来说,var声明的变量是函数级作用域,当异步请求的回调执行时,循环已经结束,所有回调共用同一个变量值,自然会出现错位的情况。
问题原因拆解
所有状态显示为Offline:
你用var声明了streamURL,它是函数级作用域的变量,每次循环都会覆盖这个变量的值。当异步的$.getJSON回调执行时,streamURL已经变成了最后一个频道的请求地址,所以所有流状态请求都是针对最后一个频道的——如果这个频道刚好是离线状态,所有主播的状态就都会显示Offline。除第一个logo外边框样式无变化:
同样,var i是函数级作用域,循环结束后i的值等于channels.length(也就是5)。当回调执行时,你用$("#img"+i)选择元素,实际上找的是不存在的#img5,根本无法给正确的logo添加样式。第一个logo偶尔生效只是因为回调刚好在循环还没结束时执行,属于偶然情况,完全不可靠。
解决方案
下面提供三种可行的解决方式,推荐优先使用前两种(ES6+语法,更简洁清晰):
方案1:使用let替换var(最简单的ES6+方案)
let是块级作用域变量,每次循环都会创建新的变量绑定,确保每个异步回调拿到的都是当前循环的变量值:
$(document).ready(function() { const channels = ["freecodecamp", "wudijo", "ThijsHS", "HSdogdog", "Sjow"]; // 用let声明循环变量i,确保每次循环的i独立 for (let i = 0; i < channels.length; i++) { // 用const/let声明url,避免变量覆盖 const channelURL = `https://wind-bow.gomix.me/twitch-api/channels/${channels[i]}?callback=?`; const streamURL = `https://wind-bow.gomix.me/twitch-api/streams/${channels[i]}?callback=?`; $.getJSON(channelURL, function(data1) { const logo = data1.logo; const name = data1.display_name; const twitchLink = data1.url; let status; $.getJSON(streamURL, function(data2) { status = data2.stream === null ? "Offline" : data2.stream.channel.status; // 先创建元素,再操作,避免全局查找id const $row = $(`<div class="row"> <div class="col-md-4"> <a href="${twitchLink}" target="_blank"> <img id="img${i}" src="${logo}"> </a> </div> <div class="col-md-4"><p>${name}</p></div> <div class="col-md-4"><p id="status${i}">${status}</p></div> </div>`); $("#followerInfo").append($row); // 直接从刚创建的row里找img,更高效可靠 const $img = $row.find(`#img${i}`); $img.addClass(data2.stream === null ? 'img-offline' : 'img-online'); }); }); } });
这里还优化了两个细节:
- 使用模板字符串拼接HTML和URL,可读性更强
- 直接从创建的
$row元素里查找logo,不用全局通过id查找,性能更好
方案2:使用forEach遍历数组(逻辑更清晰)
forEach的回调函数每次都会创建新的作用域,天然避免变量共享问题:
$(document).ready(function() { const channels = ["freecodecamp", "wudijo", "ThijsHS", "HSdogdog", "Sjow"]; channels.forEach((channel, index) => { const channelURL = `https://wind-bow.gomix.me/twitch-api/channels/${channel}?callback=?`; const streamURL = `https://wind-bow.gomix.me/twitch-api/streams/${channel}?callback=?`; $.getJSON(channelURL, function(data1) { const logo = data1.logo; const name = data1.display_name; const twitchLink = data1.url; let status; $.getJSON(streamURL, function(data2) { status = data2.stream === null ? "Offline" : data2.stream.channel.status; const $row = $(`<div class="row"> <div class="col-md-4"> <a href="${twitchLink}" target="_blank"> <img id="img${index}" src="${logo}"> </a> </div> <div class="col-md-4"><p>${name}</p></div> <div class="col-md-4"><p id="status${index}">${status}</p></div> </div>`); $("#followerInfo").append($row); const $img = $row.find(`#img${index}`); $img.addClass(data2.stream === null ? 'img-offline' : 'img-online'); }); }); }); });
方案3:立即执行函数表达式(IIFE,兼容旧环境)
如果需要兼容不支持ES6的浏览器,可以用IIFE把每次循环的变量封装起来,形成独立的闭包:
$(document).ready(function() { var channels = ["freecodecamp", "wudijo", "ThijsHS", "HSdogdog", "Sjow"]; for (var i = 0; i < channels.length; i++) { // 用IIFE保存当前循环的i和channel值 (function(currentIndex, currentChannel) { var channelURL = "https://wind-bow.gomix.me/twitch-api/channels/" + currentChannel + "?callback=?"; var streamURL = "https://wind-bow.gomix.me/twitch-api/streams/" + currentChannel + "?callback=?"; $.getJSON(channelURL, function(data1) { var logo = data1.logo; var name = data1.display_name; var twitchLink = data1.url; var status; $.getJSON(streamURL, function(data2) { status = data2.stream === null ? "Offline" : data2.stream.channel.status; var $row = $("<div class='row'><div class='col-md-4'><a href='" + twitchLink + "' target='_blank'><img id='img" + currentIndex + "' src='" + logo + "'></a></div><div class='col-md-4'><p>" + name + "</p></div><div class='col-md-4'><p id='status" + currentIndex + "'>" + status + "</p></div></div>"); $("#followerInfo").append($row); var $img = $row.find("#img" + currentIndex + ""); $img.addClass(data2.stream === null ? 'img-offline' : 'img-online'); }); }); })(i, channels[i]); } });
最后提个小细节:target='blank'应该写成target="_blank",这是HTML的标准写法,避免浏览器解析异常。
内容的提问来源于stack exchange,提问作者Zsolax

