0

I tried looking at other questions but I can't find a proper solution. I'm retrieving a GET response and I'm storing it into a variable, then I'm parsin that array in order to find daily occurrences. The code works, but once it reaches the forEach it complains with:

Cannot read property 'forEach' of undefined

. Here's my code:

var myObj;
fetch('https://blashblashblash.com?param1')
.then(res=>res.json())
.then(data=>myObj= data)
.then(() => console.log(myObj));

var myRes = [];

myObj.forEach(function (elem) {
    var date = elem.CreationDate.split(' ')[0];


    if (myRes [date]) {
        myRes [date] += 1;
    } else {
        myRes [date] = 1;
    }
});

That's the content of myObj once it finished:

[{Id, Var1, Var2, CreationDate},
{1, 123, Var2, 2020-12-11},
{2, 1234, Var2, 2020-12-12},
{3, 12345, Var2, 2020-12-12},
{4, 1234, Var2, 2020-12-13},
{5, 321, Var2, 2020-12-15},
{6, 3214, Var2, 2020-12-15},
{7, 5432, Var2, 2020-12-16}]

where am I wrong? If I'm executing the code in different steps it works correctly.

EDIT: Here's my updated code:

var myObj;
var myRes= [];
fetch('https://blashblashblash.com?param1')
.then(res=>res.json())
.then(data=>myObj= data)
.then(() => {
myObj.forEach(function (elem) {
    var date = elem.CreationDate.split(' ')[0];
    if (myRes[date]) {
        myRes[date] += 1;
    } else {
        myRes[date] = 1;
    }
});

});

var finalResult= {};
dates.forEach(date => {
    if(!myRes.hasOwnProperty(date)) {
        finalResult[date] = 0;
    } else {
        finalResult[date] = myRes[date];
    }
    return finalResult;
});

If I'm executing the whole my finalResult is empty, while if I'm executing it in blocks it works. If I'm inserting the myRes =[] inside the last .then it complains it can't find it. Where am I wrong?

thranduil90
  • 63
  • 1
  • 10
  • 1
    You can't return the data from a fetch like this. Either put everything relying on `myObj` in a then, or use aysnc/await. – evolutionxbox Dec 17 '20 at 11:49
  • but this is working correctly if I'm executing the code in two steps. How can I solve? – thranduil90 Dec 17 '20 at 11:49
  • 1
    Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – evolutionxbox Dec 17 '20 at 11:49
  • @evolutionxbox I tried but I cannot understand how to solve. In need to perform some operations on that object once I have retrieved the values in it – thranduil90 Dec 17 '20 at 11:50
  • 1
    You can. Either do the operations inside an attached then, or await the fetch. – evolutionxbox Dec 17 '20 at 11:52
  • like `async function fetchJson() { var myObj; fetch('https://blashblashblash.com?param1') .then(res=>res.json()) .then(data=>myObj= data) .then(() => console.log(myObj)); // waits until the request completes... }var myRes = []; myObj.forEach(function (elem) { var date = elem.CreationDate.split(' ')[0]; if (myRes [date]) { myRes [date] += 1; } else { myRes [date] = 1; } });` – thranduil90 Dec 17 '20 at 11:55
  • Try `var myObj = await fetch ...` and remove `.then(data=>myObj= data) .then(() => console.log(myObj));` – evolutionxbox Dec 17 '20 at 11:56

2 Answers2

1

Because your fetch & then is a async operation, you need to execute forEach in then function like below:

var myObj;
fetch('https://blashblashblash.com?param1')
.then(res=>res.json())
.then(data=>myObj= data)
.then(() => {
    console.log(myObj);
    var myRes = [];

    myObj.forEach(function (elem) {
        var date = elem.CreationDate.split(' ')[0];


        if (myRes [date]) {
            myRes [date] += 1;
        } else {
            myRes [date] = 1;
        }
    });
});
Kevin Zhang
  • 1,012
  • 6
  • 14
1

Edit

Duplicated: you might prefer this answer.

Edit: accessing response from outside of then()

Short answer:

You can't.

Long answer:

What you are doing is an asynchronous operation. It means that we don't know how long it will take.

Given an example code below, let's say fetch() takes 5 seconds to complete.

fetch('https://blashblashblash.com?param1').then(doOtherThings);

someTaskAfterFetch();

console.log('Done');

How long will it take until you see 'Done' in console after you run this code? The answer is immediately.

What's happening?

Let's see what happens.

    ===       fetch(url).then(doOtherThings) --> 'Start' fetching, without waiting for result.
     |               |
     |               |                       --> You expect fetch to be done here, which will not happen.
     |               |
     |        someTaskAfterFetch()           --> At this moment you see no fetch result.
almost 0 sec         |
     |               |
     |        console.log('Done')
     |               |
    ===       'Done' is printed
     |               |
     |               |
about 5 sec   (in some future)
     |               |
     |               |
    ===       **Fetch completed**             --> Fetch is finished here.
                     |
              doOtherThings(result)           --> doOtherThings, which we passed to `then()` will be run here.

Execution starts with fetch(url).then(doOtherThings) at line 1.

Then immediately continues to someTaskAfterFetch() at line 2.

Right after that, moves to console.log('done').

In a near future(about 5 seconds after), the fetch you started finally completes, and doOtherThings is called with the result.

How to deal with the result?

You have to separate calling and waiting when thinking about an asynchronous task. You started a fetch task by calling fetch() but didn't wait for the result.

To handle the fetch result:

  • You can wait until a task finishes(async/await)
  • Or tell what to do when fetch is completed.

Using then() is the second approach. You can pass all of your result handling code to then().

For more detailed answer, you might like this.


In your code,

fetch('https://blashblashblash.com?param1')
.then(res=>res.json())
.then(data=>myObj= data)
.then(() => console.log(myObj));

This line will return immediately.

Right after that,

myObj.forEach(function (elem) {
    var date = elem.CreationDate.split(' ')[0];


    if (myRes [date]) {
        myRes [date] += 1;
    } else {
        myRes [date] = 1;
    }
});

The forEach will be called.

At that moment, requesting a fetch job is quickly finished before the forEach. However, the fetching itself might not have finished at that time.

The functions you passed to the following then() calls will be launched after the fetching is successfully finished.

You can pass your forEach code to the then() method:

var myObj;
fetch('https://blashblashblash.com?param1')
.then(res => res.json())
.then(data => myObj = data)
.then(() => console.log(myObj));
.then(() => {
    var myRes = [];

    myObj.forEach(function (elem) {
        var date = elem.CreationDate.split(' ')[0];


        if (myRes [date]) {
            myRes [date] += 1;
        } else {
            myRes [date] = 1;
        }
    });

    // And other stuffs to do with myRes.
})
Potados
  • 177
  • 1
  • 9