我正在应对一个挑战,必须显示脱机和在线的Twitch channel 。这是存在错误的函数:
function loadStreams(){
for (var i = 0; i < channel_list.length; i++){
offlineName = channel_list[i];
console.log("offline name is: " + offlineName);
URL = "https://wind-bow.hyperdev.space/twitch-api/streams/" + channel_list[i] + "?callback=?";
$.getJSON(URL, function(data){
console.log("Now offline name is: " + offlineName);
console.log(data);
if (data.stream !== null){
currChannel = new Channel(data.stream.channel.display_name, data.stream.channel.status);
}
else {
currChannel = new Channel(offlineName, "Offline");
}
outArr.push(currChannel);
});
}
//showAll();
}
channe_list是一个预装有 channel 名称的字符串数组。定义如下:
var channel_list = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
我的代码只是通过channel_list,获取JSON数据,然后返回结果并创建Channel对象的新实例,定义为:
var Channel = function(name, status){
this.name = name;
this.status = status;
}
由于某些原因,在我的else块中,变量“offlineName”总是被channel_list数组的最后一个值'noobs2ninjas'覆盖。换句话说,当我在else块中创建Channel类的实例时,“offlineName”始终为“noobs2ninjas”。请让我知道我在这里做错了。如果您想了解一下整个内容,这是我的CodePen:
https://codepen.io/tcao2/pen/XNbbbm?editors=1010
最佳答案
这是你的问题
您可能已经知道for
循环运行的速度有多快(嗯,它非常快),但是与之相比您的网络根本不快,这就是导致您出现问题的原因。让我解释一下
您正在将$ .getJSON与URL结合使用,URL的值取决于offlineName
,但您也在成功回调中也使用offlineName
。 offlineName
是“ESL_SC2”,现在ajax请求在URL中使用它,但是通常情况下由于网络延迟,响应不会立即到达,同时循环正在进行第二次迭代。但是现在offlineName
是“OgamingSC2”!并且将被使用,这样当您的第一个请求的成功回调完成但等待更多的条目时,即使是“OgamingSC2”也将在以后消失。此外,循环是如此之快,以至于第一个或第二个响应进入时,您的循环已经在其最后一次迭代中,因此只有最终的offlineName
值(noobs2ninjas)得以保留,然后将其用于所有其他对象的成功回调中。
解决方案:解决方案是找到某种方式来保留每次迭代的offlineName
值,并在相应的成功回调中使用该值。最简单的方法是使用let
声明URL
和offlineName
,从而限制了每次迭代的本质提供类似于闭合的效果
https://codepen.io/vsk/pen/LbNpBQ
上面代码的唯一问题是let
是最近添加的,而较旧的浏览器并不很好地支持它,因此另一种解决方案是针对每个传递URL
和offlineName
的请求实际实现关闭
(function(url,name) {
$.getJSON(url, function(data){
if (data.stream !== null){
currChannel = new Channel(data.stream.channel.display_name, data.stream.channel.status);
}
else {
currChannel = new Channel(name, "Offline");
}
outArr.push(currChannel);
});
})(URL,offlineName);
https://codepen.io/vsk/pen/rWeOGL
编辑:这些被称为自执行函数,它们之间没有什么特别的地方,只是下面代码的简写形式
function hello(url,name){ //line #39
//your code
} //ln #53
hello(URL,offlineName); //ln #54
看到this,您会发现它运行完美,但是当您注释掉该函数(第39、53、54行)时,它又恢复为旧的错误行为。您可能想知道一个简单的函数将如何如此剧烈地改变行为这是方法-全部基于范围链
就像Java一样,JS解释器(以下称为VM)现在到达
hello
的定义时逐行读取您的代码,它只是读取它(研究参数,返回值和内部代码),然后继续;现在已经到达 call hello(URL,offlineName);它在hello中运行代码,但随后意识到getJson
具有当前无法调用的回调,因此将其记录在“稍后将被调用”列表中,以及该函数中使用的所有变量的值,那时 [1]。因此,即使在以后的循环迭代中URL
和offlineName
被重新初始化/分配了新值,它们也不会影响[1]中绑定(bind)的值,因为它们与它们无关,它们是完全不同的实体。这是因为JS按值传递参数(至少对于原始类型)但是关于作用域链的最重要的事情是,即使循环超过了
getJson
回调中引用的值,仍然存在唯一的问题是您不能直接访问它们,但是VM可以访问。原因是-链中的最后一个功能是一个回调(记录在列表中),因此要想使VM在将来运行时必须让它生存所需的值, Nerd 称其为闭包,内部函数始终可以访问外部函数中存在的内容,甚至认为外部函数调用已结束,控制权又返回到其他地方。请注意,即使在您早期的错误代码值中都得到了保存,唯一的问题是它们被覆盖了,因为对于它们而言,它们所有人都只有一个外部函数,即loadStreams
,但是当您创建并调用单独的hello
时每个人都会创建一个单独的环境(类似于并行世界)。本质上,它创建作用域链,因此每次迭代都可以拥有不受他人干扰的“自己的空间”。
for loop --> hello() --> getJson's inner function
(每次迭代)您可能会喜欢
let
,但首先请查看http://caniuse.com/#feat=let的兼容性表关于javascript - getJSON函数中覆盖的变量值,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40559324/