0

I've been trying to add values to my data array, however I came across the problem, that it goes to data.push() earlier than the value from getValue function is returned. I've been trying to fix it, but couldn't come up with the solution and feel kind of lost here.

this is the function where I form data:

formData() {
  for (const id of Object.keys(this.cols)) {
    let p= cols[id];
    switch (p.name) {
      case 'a':
      this.temp.name= this.addValue(m, p);
        break;
      case 'b':
        let idx = 1;
        let surname= this.getValue(idx);
        break;
    }
  }
  this.data.push(this.temp);
});

and this is how my getValue function looks like:

 getValue(id: string) {
let url = this.baseUrl + '/streams/' + id+ '/data?';
url += 'from=' + from + '&';
url += 'to=' + to;
this.http.get(url).map(r => r.json()).subscribe((response: any) => {
        if (response.data.values[0] !== null && response.data.values[0] !== undefined) {
         return response.data.values[0];
        }
}, (error: any) => {
    return null;
});

how can I avoid pushing data to array before that value is actually there?

user122222
  • 2,179
  • 4
  • 35
  • 78
  • 2
    Possible duplicate of [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) – tymeJV Feb 12 '19 at 21:59
  • 2
    You don't subscribe in the service. The service returns an Observable, and the component subscribes. Read the angular documentation on RxJS and observables. – JB Nizet Feb 12 '19 at 22:05
  • There's a *lot* going on in this question. You're looping over an array where each loop may or may not require an async call to a service and then you want to wait for all async calls to have resolved before doing something at the end of the loop! – Simon K Feb 12 '19 at 22:12
  • You could (and should) probably subscribe in `formData()` but to provide an answer you would have to show the full code of `formData()` (you don't seem to). In `this.addValue(m, p)` where is `m` coming from? In `let surname= this.getValue(idx);` what are you actually doing with the returned value? `surname` is never used! `for (const id of Object.keys(this.cols))` could be `for (const p of Object.values(this.cols))` if you only ever use the `id` to get a value from `this.cols`. – frido Feb 12 '19 at 23:03

2 Answers2

1

If this were a less complicated question, I would fully support closing this question as a duplicate but, as my comment on the question says, it's a little more complicated that that.

For starters, you should definitely read up on async functions and how to use responses from them. Next up, you should generally try to avoid using an async function inside of a loop as they can get difficult to predict and control. Fortunately, there are ways to get async functions to behave more syncronously.

Observe the following:

async formData(): Promise<void> {
  for (const id of Object.keys(this.cols)) {
    let p= cols[id];
    switch (p.name) {
      case 'a':
      this.temp.name= this.addValue(m, p);
        break;
      case 'b':
        let idx = 1;
        let surname = await this.getValue(idx);
        break;
    }
  }
  this.data.push(this.temp);
});

getValue(id: string): Promise<string> {
    return new Promise(resolve => {
        let url = this.baseUrl + '/streams/' + id+ '/data?';
        url += 'from=' + from + '&';
        url += 'to=' + to;
        this.http.get(url).map(r => r.json()).subscribe((response: any) => {
            if (response.data.values[0] !== null && response.data.values[0] !== undefined) {
                resolve(response.data.values[0]);
            }
        }, (error: any) => {
            resolve(null);
        });
    });
}

Your call to getValue is an async call as it required a delay (the call to the server) before the value can be returned. I've wrapped this in a Promise as you wanted both success and error paths of the subscription to return a value.

If we mark the formData method with the async keyword we can now use the await keyword within the method. This will cause the flow of the method to await the return from the getValue promise before control continues.

Side-not: by marking the formData method with async, it makes the return-type of the method a Promise even though you aren't directly returning anything from the method.

Simon K
  • 2,762
  • 1
  • 11
  • 20
0

how can I avoid pushing data to array before that value is actually there?

chain it off the subscribe e.g.

this.http.get(url).map(r => r.json()).subscribe((response: any) => {
        if (response.data.values[0] !== null && response.data.values[0] !== undefined) {
         // NOTE
         this.loadFormData();
        }
basarat
  • 261,912
  • 58
  • 460
  • 511
  • On recent versions of angular this type of HTTP request doesn't works, .map() is not required since ...if I'm not wrong, 5...and also, the subscribe(response: any) should not be casted to "any"... :) – Asfo Feb 13 '19 at 00:21