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

Console.log repeating output in each getJSON loop

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

它与 FCC Twitch 应用程序的功能相似 here。不同之处在于我交换了 .$getJSON 订单。他们的应用程序首先获得 'stream',而我首先获得 'channel'。我一直在比较我的代码和他们的代码,无法理解为什么内部 .$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
            });
        });
    }
}

因为 $.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){ ... }

因为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、徽标等)的声明移动到我们刚刚创建的 forEach 中也是明智的。

参考文献: