0

So im trying to make a request to an API using a foreach loop on an array. The array contains a list of strings that are placed into the request

var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");

var urlencoded = new URLSearchParams();
urlencoded.append("", "");

var requestOptions = {
  method: 'POST',
  headers: myHeaders,
  body: urlencoded,
  redirect: 'follow'
};

var array = [];
var data = [];

var importdata =  new Promise((resolve, reject) => {
    $.getJSON('name of json.json')
    .then(response => {
        data = response;
        resolve(response)
    })
    .catch(err => {
        reject(err)
    })
})

var getAPIResults = new Promise((resolve, reject) => {
    var xmlHttp = new XMLHttpRequest();
    data.forEach((element) => {
        console.log(element) // This isn't being outputted into the console, even though 'data' array has elements after running 'importdata'
        xmlHttp.open( "GET", 'http://api.com/json/apiroute?key=xxxx-xxx-xxxx&dataitem='+element, true );
        xmlHttp.send( null );
        array.push(xmlHttp.responseText)
    })
    resolve(xmlHttp.responseText)
})


async function main () {
    await importdata
    await console.log(data)
    await console.log(getAPIResults)
}

main()

I have added a comment where the data isn't being logged

Here is what the console looks like:

{Data: {…}}
Data:
   data: (1030) [expected data elements]
   __proto__: Object
   __proto__: Object

Promise {<fulfilled>: ""}
   __proto__: Promise
   [[PromiseState]]: "fulfilled"
   [[PromiseResult]]: ""

Surely there should be a long list of elements (the data array is around 1000 elements long) when getAPIResults is ran?

Adam Cole
  • 27
  • 11
  • Why do you use XHR not `$.json`? – Bergi Feb 13 '21 at 18:50
  • You meant to use `console.log(await getAPIResults)`, not `await console.log(getAPIResults)` – Bergi Feb 13 '21 at 18:51
  • Btw, when wrapping jQuery deferreds in real promises, you can avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it) and [just write `Promise.resolve($.json(…))`](https://stackoverflow.com/a/31327725/1048572). – Bergi Feb 13 '21 at 18:54

1 Answers1

1

You're awaiting the console.log (which isn't asynchronous), but not awaiting the getAPIResults Promise (which is asynchronous). Just as you did when you awaited importdata, you also need to await getAPIResults:

await importdata;
console.log(data);
console.log(await getAPIResults);

Though it appears there are further problems within getAPIResults itself. Internally it's sending AJAX requests, which are also asynchronous, but it's trying to immediately use their responses. You'll need to continue to repeat the pattern of awaiting promises, or perhaps using callbacks.

At the very least the console.log(element) within getAPIResults will do what you're looking for, as asked in the question. But your next problem will be that array.push(xmlHttp.responseText) is pushing no data to the array and resolve(xmlHttp.responseText) is resolving nothing from the promise. (And don't you want to resolve to the array, not just the last API result?)


Edit: Regarding the AJAX operations taking place within getAPIResults, you'd want to treat them asynchronously just like you already do with importdata. In fact, why not just use $.getJSON like you already do? Perhaps something like this:

var getAPIResults = new Promise((resolve, reject) => {
    var promises = [];
    data.forEach((element) => {
        console.log(element);
        promises.push(
            $.getJSON('http://api.com/json/apiroute?key=xxxx-xxx-xxxx&dataitem='+element)
            .then(response => {
                array.push(response);
            });
        );
    });
    Promise.all(promises).then(() => {
        resolve(array);
    });
});

The idea here is that you loop through the array of data you have, invoking an AJAX request for each. $.getJSON returns a Promise, so you add all those promises to an array. Then use Promise.all to await all of those promises and, when they're complete, resolve getAPIResults to the resulting data.

As an aside, if it's an option you may want to start using async/await syntax. It would clear up a lot of this and make the code easier to follow. Instead of making things like getAPIResults a Promise, you'd make it a function which happens to be async. Something as simple as:

var getAPIResults = async () => {
    const result = [];
    for (const element of data) {
        // I don't think jQuery's AJAX functions return actual promises.
        // So I've wrapped it in one here.  Which is inelegant, but should work.
        // If they DO return actual promises these days, unwrap this.
        // It's worth testing both approaches for your needs.
        const response = await new Promise(resolve => {
            $.getJSON('http://api.com/json/apiroute?key=xxxx-xxx-xxxx&dataitem='+element)
            .then(json => {
                resolve(json);
            });;
        });
        result.push(response);
    }
    return result;
};

Then consuming that function would be:

console.log(await getAPIResults());

You'll also find that passing values to and returning values from functions is much cleaner than relying on global variables. Note how this version doesn't push to the global array variable but just returns the expected array. You could also make importdata a function which returns its result, and pass that result to getAPIResults as a function argument.

David
  • 176,566
  • 33
  • 178
  • 245
  • Thanks for your response. I'm not understanding why array.push won't push any data to my array. Do you mean that i should be using `.then(res => {})` after the `xmlHttp.send`? (I just tried that and it didn't work) – Adam Cole Feb 13 '21 at 19:07
  • Also - my console.log still isn't doing anything – Adam Cole Feb 13 '21 at 19:07
  • @AdamCole: Are there any errors on the console? If `data` is an array then awaiting `getAPIResults` should at the very least iterate through that array and log its elements to the console. Pay specific attention to the structure of `data`. Is it an array, or is it an object with a *property* (also called `data`) which is an array? You'll need to debug to find out. As for the AJAX requests, the question I linked to in my answer has some examples of how to wrap `XMLHttpRequest` in a Promise. Though why don't you just use `$.getJSON` like you already did before? Then you can append `.then()`. – David Feb 13 '21 at 19:55
  • @AdamCole: I've updated the answer with some more code demonstrating how you might await the series of AJAX requests being made in `getAPIResults`. – David Feb 13 '21 at 20:16
  • Your edit fixed my code, thanks! Now I have hit the limit of my free trial on the Fetchify API so this code is pretty much useless to me now ‍♂️ thanks for your help though! – Adam Cole Feb 13 '21 at 21:24