1

I created a function that makes a series of API calls through a Promise like this

//userApiList is a variable with an array of links
return Promise.all(userApiList.map(url =>{
    var a = $.getJSON(url);
    //userData is a global variable
    userData.push(a);
    return a;
}));

I want to both save the data to a variable for later use and return it right away to be iterated into html with jquery. Everything loads up perfectly as expected, however when I go to access the data in the variable I get all "undefined" properties. I console.logged the variable and it shows the data is there, I click the buttons well after the data is fetched so it's not an async issue. here's an example of one of the functions

$("#load_online").click(function(){
    var users = [];

    for(var i = 0; i < userData.length; i++){
      var status = userData[i].status;
      if(status !== null || status !== undefined){ users.push(userData[i]); }
    }
    $("#result_frame").empty();
    //loadUsers() iterates the data into html
    loadUsers(users);
});

I tried console.log(userData[i].status) just to see what the results would be and I got 200 as a response when it's suppose to be null' or the title of an episode being streamed on the channel.

The problem is I thought the responseJSON: field is what's always returned for use, as I've never walked into this issue before. This time it seems the whole object is being read therefore userData[i].status was reading the status: property a layer up rather than inside the responseJSON: object. I tried thinkering with getting a response out of userData[i].responseJSON.status and that returned undefined for each object. Does anybody readily see what I'm doing wrong? here's a CodePen of the overall project if you need a closer look at things.

Optiq
  • 2,835
  • 4
  • 33
  • 68
  • 1
    You should have a look at [How do I return the response from an asynchronous call?](https://stackoverflow.com/q/14220321/218196) – Felix Kling May 08 '18 at 18:11
  • From your commentts, you're having trouble applying the answers to your code. Please update your question with a [mcve] demonstrating the problem, ideally a **runnable** one using Stack Snippets (the `[<>]` toolbar button; [here's how to do one](https://meta.stackoverflow.com/questions/358992/ive-been-told-to-do-a-runnable-example-with-stack-snippets-how-do-i-do-tha)). (Obviously you'll have to emulate the ajax part, probably with `setTimeout`.) That way we can see what you're doing and help you fix it. – T.J. Crowder May 09 '18 at 05:07

2 Answers2

3

You're not pushing the data into your array, you're pushing a jQuery jqXHR object (which is thenable, e.g., Promise-like) into it (that's what the return value of $.getJSON is). The reason the Promise.all result works is that Promise.all waits for those thenables to resolve.

It's quite likely you don't want to have a userData global at all. Instead, have whatever code needs the data get it from a resolution handler on the the promise returned by Promise.all.

But if you really want to populate userData, wait for resolution:

return Promise.all(userApiList.map($.getJSON))
    .then(results => {
        userData.push(...results);
        return results;
    });

Note that userData won't have the data until the ajax requests complete (of course).

The above won't add the results to userData until they're all avaialble. You also have the option of populating it as you go:

return Promise.all(userApiList.map(url => $.getJSON(url).then(result => {
        userData.push(result);
        return result;
    })));

But it's important to remember the results will be added over time, not instantaneously, as the requests complete.

If you wait for all of them, Promise.all will ensure they're in the same order as the promises you giave it; if you add them as you go, they may not be in that order (because you're pushing as each one completes, and an earlier one may complete after a later one).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I'm a little confused as to the difference you'r pointing out. The first one doesn't have the `url` to pass into the `$getJSON`. The 2nd one recreates the same issue except nothing on the screen changes – Optiq May 08 '18 at 20:20
  • @Optiq: *"I'm a little confused as to the difference you'r pointing out."* As I said above: The first one populates `userData` all at once when the whole process finishes, the second populates it as each call individually finishes. *(cont'd)* – T.J. Crowder May 09 '18 at 05:06
  • *(continuing)* *"The first one doesn't have the url to pass into the $getJSON"* Yes, it does. Note I'm passing `$.getJSON` itself, directly, into `then`. It will get called by the promise resolution, passing it `url`. `.then($.getJSON)` is effectively the same as `.then(url => $.getJSON(url))` (other than what `this` is during the `getJSON` call, but `getJSON` doesn't care about `this`). *"The 2nd one recreates the same issue except nothing on the screen changes"* Then you're trying to use `userData` before it's fully populated. – T.J. Crowder May 09 '18 at 05:06
0

agreed with the upper answer,

this may work too,

return userApiList.map(url => { $.getJSON(url).done(function(results){ userData.push(results) });})