Console.log在每个getJSON循环中重复输出

时间:2021-07-11 16:56:47

Could someone tell me why my console.log is returning the same output for every loop and how to fix it? The console.log should be returning each element of the 'channels' array, but it keeps outputting the last element 'noobs2ninjas'.

有人能告诉我为什么我的console.log为每个循环返回相同的输出以及如何修复它? console.log应该返回'channels'数组的每个元素,但它会不断输出最后一个元素'noobs2ninjas'。

It is a similar function to the FCC Twitch app here. The difference is that I swapped the .$getJSON order. Their app is getting the 'stream' first while I'm getting the 'channel' first. I've been comparing my code and theirs and could not understand why the inner .$getJSON will be repeating the same array element over and over again but the outter .$getJSON is looping through each element correctly.

它与FCC Twitch应用程序的功能类似。不同之处在于我交换了。$ getJSON命令。当我首先获得“频道”时,他们的应用程序首先获得“流”。我一直在比较我的代码和他们的代码,并且无法理解为什么内部。$ getJSON将一遍又一遍地重复相同的数组元素但是outter。$ getJSON正在循环遍历每个元素。

function loadChannel() {
    var channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
    var display_name = null;
    var logo = null;
    var url = null;
    var game = null;
    var status = null;
    var online = false;

    function api(type, channel) {
        return `https://api.twitch.tv/kraken/${type}/${channel}?callback=?`;
    }

    for (var index in channels) {
        $.getJSON(api('channels', channels[index]), function(data) {
            display_name = data.display_name;
            logo = data.logo;
            url = data.url;
            game = data.game;
            status = data.status;
            $.getJSON(api('streams', channels[index]), function(data) {
                // get online status
                console.log(channels[index]); // <-- outputs same channel 'noobs2ninjas' each loop
                console.log(data); // <-- outputs same object each loop
            });
        });
    }
}

2 个解决方案

#1


1  

It's because var in Javascript is function scoped, not block scoped; your var index declaration is being hoisted to the the top of loadChannels and is overwritten each loop. This means that your async functions are only accessing the last index, since the loop finishes before the ajax calls do. There is a nice solution that you are pretty close to already though.

这是因为Javascript中的var是函数作用域,而不是块作用域;你的var索引声明被提升到loadChannels的顶部,并被覆盖每个循环。这意味着您的异步函数只访问最后一个索引,因为循环在ajax调用之前完成。有一个很好的解决方案,你已经非常接近了。

Using your existing code you could switch out just the for loop and instead utilize Array.forEach. You will pass it a function that will create a new scope with the channel scoped locally to the loop (and therefore not overwritten on each iteration).

使用现有代码,您可以只切换for循环,而是使用Array.forEach。您将向它传递一个函数,该函数将创建一个新的作用域,其中通道在本地作用于循环(因此在每次迭代时不会被覆盖)。

function loadChannel() {
    var channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
    var display_name = null;
    var logo = null;
    var url = null;
    var game = null;
    var status = null;
    var online = false;

    function api(type, channel) {
        return `https://api.twitch.tv/kraken/${type}/${channel}?callback=?`;
    }

    channels.forEach(function (channel) {
        $.getJSON(api('channels', channel), function(data) {
            display_name = data.display_name;
            logo = data.logo;
            url = data.url;
            game = data.game;
            status = data.status;
            $.getJSON(api('streams', channel), function(data) {
                // get online status
                console.log(channel); // <-- should be fixed
                console.log(data); // <-- outputs same object each loop
            });
        });
    });
}

It might also be wise to move the declaration of any other variables you plan to use inside the loop (display_name, logo, etc) into the forEach we just made.

将您计划在循环中使用的任何其他变量(display_name,logo等)的声明移动到我们刚刚创建的forEach中也是明智之举。

References:

#2


1  

Because $.getJSONis asyncronous, and by the time its callback is called, the loop is already over and index will hold the latest value of the loop.

因为$ .getJSON是异步的,并且在调用其回调时,循环已经结束并且索引将保持循环的最新值。

You should bind your function to have index as first parameter, as:

您应该绑定您的函数以将索引作为第一个参数,如下所示:

for (var index in channels) {
  (function(i){
     $.getJSON(api('channels', channels[i]), function(data){
       ...
       // channels[i] is what you want here
       $.getJSON(api('streams', channels[i]), function(data) {
         ...
       });
     });
  }.bind(null, index))()
}

But in this specific case, why not channels.forEach(function(channel){ ... }?

但在这个特定的情况下,为什么不channel.forEach(function(channel){...}?

#1


1  

It's because var in Javascript is function scoped, not block scoped; your var index declaration is being hoisted to the the top of loadChannels and is overwritten each loop. This means that your async functions are only accessing the last index, since the loop finishes before the ajax calls do. There is a nice solution that you are pretty close to already though.

这是因为Javascript中的var是函数作用域,而不是块作用域;你的var索引声明被提升到loadChannels的顶部,并被覆盖每个循环。这意味着您的异步函数只访问最后一个索引,因为循环在ajax调用之前完成。有一个很好的解决方案,你已经非常接近了。

Using your existing code you could switch out just the for loop and instead utilize Array.forEach. You will pass it a function that will create a new scope with the channel scoped locally to the loop (and therefore not overwritten on each iteration).

使用现有代码,您可以只切换for循环,而是使用Array.forEach。您将向它传递一个函数,该函数将创建一个新的作用域,其中通道在本地作用于循环(因此在每次迭代时不会被覆盖)。

function loadChannel() {
    var channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
    var display_name = null;
    var logo = null;
    var url = null;
    var game = null;
    var status = null;
    var online = false;

    function api(type, channel) {
        return `https://api.twitch.tv/kraken/${type}/${channel}?callback=?`;
    }

    channels.forEach(function (channel) {
        $.getJSON(api('channels', channel), function(data) {
            display_name = data.display_name;
            logo = data.logo;
            url = data.url;
            game = data.game;
            status = data.status;
            $.getJSON(api('streams', channel), function(data) {
                // get online status
                console.log(channel); // <-- should be fixed
                console.log(data); // <-- outputs same object each loop
            });
        });
    });
}

It might also be wise to move the declaration of any other variables you plan to use inside the loop (display_name, logo, etc) into the forEach we just made.

将您计划在循环中使用的任何其他变量(display_name,logo等)的声明移动到我们刚刚创建的forEach中也是明智之举。

References:

#2


1  

Because $.getJSONis asyncronous, and by the time its callback is called, the loop is already over and index will hold the latest value of the loop.

因为$ .getJSON是异步的,并且在调用其回调时,循环已经结束并且索引将保持循环的最新值。

You should bind your function to have index as first parameter, as:

您应该绑定您的函数以将索引作为第一个参数,如下所示:

for (var index in channels) {
  (function(i){
     $.getJSON(api('channels', channels[i]), function(data){
       ...
       // channels[i] is what you want here
       $.getJSON(api('streams', channels[i]), function(data) {
         ...
       });
     });
  }.bind(null, index))()
}

But in this specific case, why not channels.forEach(function(channel){ ... }?

但在这个特定的情况下,为什么不channel.forEach(function(channel){...}?