Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3 - Can't return data from json request?

Tags:

json

ajax

d3.js

I'm trying to use d3.json() inside of a function to return data Spotify's API given an artist ID (such as 5K4W6rqBFWDnAN6FQUkS6x), but I can't figure out how to effectively return the data. The function looks like

// Get artist's related artist's information
function relatedArtists(id){
  var jsonPromise = new Promise(function(resolve, reject) {
    // Async JSON request
    d3.json('https://api.spotify.com/v1/artists/' + id + '/related-artists', function(error, data){
      if(error) reject(error);
      resolve(data.artists);
    });
  });

  jsonPromise.then(function(success) {
    console.log(success);
    //return(success)  //doesn't work
  });

  jsonPromise.catch(function(error){
    console.error(error);
  });

}

I've tried creating a variable within the function and then modifying it

function relatedArtists(id){
  var testVar = 'hello';
  var jsonPromise = new Promise(...{
    // Async JSON request
    d3.json(...)
  });
  jsonPromise.then(function(success) {
    testVar = success;
  });
  return(testVar);
}

But testVar remains 'hello', despite my best efforts. I've done some reading about scope and promises, but am happy to do more if there's some core mechanic of either that I'm not understanding. Thanks for reading!

like image 367
Cass Avatar asked Jan 04 '23 05:01

Cass


2 Answers

The response will never be available in your calling code due to asynchronous nature of requests. You can use Promises (as supposed by Alexander T. and you, good choice in many cases!) but d3.queue does a good job, too. In my snippet you can see how to run code with the results of multiple requests.

function buildRelatedArtistUri(id) {
  return 'https://api.spotify.com/v1/artists/' + id + '/related-artists';
}

d3.queue()
  .defer(d3.json, buildRelatedArtistUri('5K4W6rqBFWDnAN6FQUkS6x'))
  .await(function(error, data) {
    // data and data.artists are available in this function‘s scope only
    console.log(data.artists);
  });

d3.queue()
  .defer(d3.json, buildRelatedArtistUri('5K4W6rqBFWDnAN6FQUkS6x'))
  .defer(d3.json, buildRelatedArtistUri('3nFkdlSjzX9mRTtwJOzDYB'))
  .await(function(error, data1, data2) {
    // this function will be called once both functions have finished
    console.log(data1.artists, data2.artists);
  });
<script src="https://d3js.org/d3.v4.min.js"></script>
like image 179
undko Avatar answered Jan 07 '23 12:01

undko


You can return Promise and use relatedArtists function like so

function relatedArtists(id) {
  return new Promise(function(resolve, reject) {
    d3.json('https://api.spotify.com/v1/artists/' + id + '/related-artists', function(error, data) {
      if (error) {
      	reject(error);
      } else {
      	resolve(data.artists);
      }
    });
  });
}


relatedArtists('5K4W6rqBFWDnAN6FQUkS6x')
  .then(function (data) {
    console.log(data);
  })
  .catch(function (error) {
    console.log(error);
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

In this case, you can not assign the value to testVar, because d3.json is the asynchronous method and that means that d3.json can be done after code execution.

like image 39
Oleksandr T. Avatar answered Jan 07 '23 12:01

Oleksandr T.