【问题标题】:Console.log repeating output in each getJSON loopConsole.log 在每个 getJSON 循环中重复输出
【发布时间】:2026-01-25 13:40:01
【问题描述】:

谁能告诉我为什么我的 console.log 为每个循环返回相同的输出以及如何解决它? console.log 应该返回“channels”数组的每个元素,但它一直输出最后一个元素“noobs2ninjas”。

它的功能类似于 FCC Twitch 应用程序here。不同之处在于我交换了 .$getJSON 顺序。他们的应用程序首先获得“流”,而我首先获得“频道”。我一直在比较我的代码和他们的代码,不明白为什么内部 .$getJSON 会一遍又一遍地重复相同的数组元素,但外部 .$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
            });
        });
    }
}

【问题讨论】:

    标签: javascript jquery twitch


    【解决方案1】:

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

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

    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
                });
            });
        });
    }
    

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

    参考资料:

    【讨论】:

    • 感谢吊装和forEach的解释。我不想直接复制他们的代码,而我最初的实现是使用常规的 for 循环并遇到了同样的问题。我认为 FCC 的代码起作用的原因是因为他们在另一个异步请求中嵌入了一个异步请求,而且 forEach 与您所揭示的 for 循环完全相同。
    【解决方案2】:

    因为$.getJSON是异步的,当它的回调被调用时,循环已经结束,index将保持循环的最新值。

    你应该bind你的函数有index作为第一个参数,如:

    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))()
    }
    

    但在这种特殊情况下,为什么不channels.forEach(function(channel){ ... }

    【讨论】:

    • FCC 的应用程序如何解决这个问题?我在每个 getJSON 之后插入了 console.log 并且它没有在他们的代码中重复相同的元素?
    • 因为他们使用.forEach
    • 谢谢。我认为他们的代码工作的原因是因为他们在另一个异步请求中嵌入了一个异步请求。