How to retrieve multiple JSON objects with a for l

2019-09-08 11:09发布

问题:

I've been playing around with the Last.fm API and JSON and I've been trying to retrieve a user's top artists by month for the past 12 months. I tried to set up a for loop to go through each month and then pull the relevant JSON data corresponding to that month, but from what I can tell it seems like the for loop is being run through much quicker than the JSON call.

I'm using Felix Bruns' last.fm javascript API https://github.com/fxb/javascript-last.fm-api

I checked the console and no values of month were logged except for 12. I'm also getting an Uncaught Reference Error "json##.... is not defined"

I tried looking around for solutions but all of my search results came up as how to loop through the results of an API call whereas I'm looking for how to write a loop that retrieves multiple JSON objects.

<script type="text/javascript">

  var apiKey = "b0da1774db3d010f62b11f67c4de0667";
  var secret = "0baa4b10c807acc847128599680679a7";

  var lastfm = new LastFM({
    apiKey : apiKey,
    secret : secret,
    cache : undefined
  });

  var lastfm_2 = new LastFM({
    apiKey : apiKey,
    secret : secret,
    cache : undefined
  });

  $(document).ready(function() {
    $("#submit").click(function() {
      var username = $("#username").val();
      var text = "";
      if (username) {
        $("#title").html("Your Most Played Artist by Month");
        $("#title").css("color", "#222");
        // Get top artists for each month
        var topArtistsByMonth = new Array();
        for (var month = 0; month < 12; month++) {
          lastfm.user.getTopArtists({user: username, period: "1month", limit: 15, page: month + 1}, {success: function(data) {
            topArtistsByMonth.push(data.topartists);
            console.log("Month " + month + ": " + data.topartists);
          }});
        }
      } else {
        alert("No username");
      }
    });
  });

</script>

Any help would be appreciated, thanks!

回答1:

getTopArtists is asynchronous, so calling it only starts the request; it doesn't wait for it to finish. The callback is how you know when it's done. That means that your for loop fires them all off in parallel, and then you collect the results when you're done. However, since they can finish in any order, topArtistsByMonth is not guaranteed to be in any sort of order. To fix that, you'll probably want to make it use explicit indices rather than using push:

for(var month = 0; month < 12; month++) {
    // We need to use an anonymous function to capture the current value of month
    // so we don't end up capturing the reference to month that the for loop is
    // using (which, by the time the callbacks complete, will always be 12.)
    (function(month) {
        lastfm.user.getTopArtists({user: username, period: "1month", limit: 15, page: month + 1}, {success: function(data) {
            topArtistsByMonth[month] = data.topartists;
            console.log("Month " + month + ": " + data.topartists);
        }});
    })(month);
}

If you want to know when all the data has been downloaded, you'll need another variable to keep track of how many have finished so far. Every time the callback is called, you'll need to increment that and see if it's hit 12 yet. When it has, all the data has been downloaded.