0

I'm currently trying to empty an array and then fill it with information grabbed from an API upon every iteration that the overlying function is called for, all in JavaScript.

function getInfo() {
    clearArray()

    // call the API
    callAPI()

    // push arrayObjects into DOM
    array.forEach(object => {
        // do stuff with it
    }
}

clearArray() sets the length of the array to zero:

function clearArray() {
    array.length = 0
}

callAPI() is a fetch function:

function callAPI() {
    fetch(urlG)
        .then(response => response.json())
        .then(function(response) {
            // parse and then push objects into global array
        })
}

I'm pretty sure that the callAPI() function is doing it's job correctly from console.log()-ing at various points.

However, when I console.log(array.length) right before the forEach statement, it turns up to be 0. I read that console.log may not be the most accurate in this case, but:

  • Adding a console.log(array) right after the clearArray() function - but before callAPI() - prints out the correctly filled out array in the console.
  • Same thing for adding the console.log(array) after callAPI() is called.
  • console.log(array.length) anywhere results in 0.
  • Using breakpoints before and after each function is called always shows the array being empty.

I tried using a callback to force it to wait, like this:

callAPI(callback)

function callAPI(callback) {
    fetch(url)
        .then(response => response.json())
        .then(function(response) {
            // do stuff
        })
    callback()
}

getInfo():

function getInfo() {
    // reset news array
    clearArray()

    // call the news APIs
    callAPI(function() {

        console.log(array.length)
        // push arrayObjects into DOM
        array.forEach(arrayObject => {
            // do stuff
        })
    }
}

But I'm running into the same error.

Using await for fetch(url) ends up in the same exact issue.

async function callAPI() {
    let response = await fetch(url)
    let json = response.json()
    let results = json.response.results
    // do stuff
}

I'm still relatively unfamiliar with JavaScript, so if any further information is needed then I can absolutely supply it.

Joseph
  • 680
  • 1
  • 5
  • 10
  • Show actual code or if actual code is restricted create a [mcve] that represents the problem. If the clear and callAPI functions are not async (nor use async requests), are called in turn, and both functions are accessing the same array there shouldn't be a problem. – Patrick Evans Nov 20 '19 at 22:18
  • Are you sure callApi is not using some async request through `fetch()` or `XMLHTTPRequest`. The latter needs to be specifically told to make a sync request. – Patrick Evans Nov 20 '19 at 22:24
  • 1
    Sorry - callAPI is using fetch, but the reason why I said it wasn't async was because I tried using ```await``` in front of the fetch(url) and the console gave me a "await is only valid in async function" error on that line. I tried using a callback on the callAPI function as well, but that still ended up in array.length being 0. I'll update my question to show better code including the callback that I attempted, thanks for the warning. – Joseph Nov 20 '19 at 22:30
  • 1
    Okay, so I realized that I needed to mark the function as async, so I successfully put await before the fetch and when it converts the response to json. But the array is still turning up empty, the same issue. – Joseph Nov 20 '19 at 22:45

2 Answers2

1

In the callback situation it has to be called from one of the then. And in the callback use array.forEach

function handleData(array){
  array.forEach(...);
}

function callAPI(callback) {
    fetch(url)
        .then(response => response.json())
        .then(function(response) {
          callback(response)
        })
        //or this, both are equivalent
        then(callback)
}

callAPI(handleData);

For the async/await (promises) you need to make callAPI an async function and use it properly:

async function callAPI() {
    let response = await fetch(url)
    //json() also returns a promise so you have to await that as well
    let array = await response.json();
    array.forEach(...);
}

Or

async function callAPI() {
    let response = await fetch(url)
    let array = await response.json();
    return array;
}

callAPI().then(function(array){
  array.forEach(...);
})
Patrick Evans
  • 41,991
  • 6
  • 74
  • 87
  • 1
    Thank you, that fixed it. I'm still terrible with the whole async concept, but your answer helped me understand it better. Also - thanks for sticking through with my subpar question skills. – Joseph Nov 20 '19 at 22:52
1

The reason why your code behaves like that is because javascript does not stop when it sees an asynchronous code.

So this is what's happening.

When JavaScript enters your function getInfo, it executes clearArray and goes to the next instruction callAPI. It then sees that callAPI contains asynchronous code. It then sends that piece of asynchronous code right away to the Web API to handle that so it doesn't wait. It then goes to the next instruction forEach (while the asynchronous code is still executed by the Web API). And at that time the promise in the async code is not resolved yet which means that the objects are not pushed yet into your global array, so the array is still empty.

So here is a way to fix that by using async/await in the callAPI function:

function async callAPI() {
    const apiResponse = await fetch(urlG) //pause and get apiResponse before continuing execution
    const jsonResponse = await apiResponse.json() //pause and get jsonResponse before continuing execution

    //parse and then push objects into global array
    //...
}
Bradzer
  • 146
  • 2
  • 10