0

I'm desperated with this code.

getSumOfSpecificDayWeek(daysMonth: any, callback: any){
    var data = [];
    var that = this;
    daysMonth.forEach(function(day){
      that.statsService.getData(that.userid, day).subscribe(async (res: any) => {
        data = JSON.parse(JSON.stringify(res));
        console.log(that.data);
        that.data = that.data.map( function(v, i) {
          return v + data[i];
        });
      });
    });
    callback("this should be at the end");
  }

Here what I'm doing is getting arrays from a server and summing it up into that.data per each component of it, this works fine but at the end I want to average the result, at this very moment I'm just calling to callback to show a message to checking whether it occurs finally, but no, "this should be at the end" is displayed before the loop starts summing.

  mycallback(arg: any){
    console.log(arg);
  }

This is the main call to the method

this.getSumOfSpecificDayWeek(daysMonth, this.mycallback);
Sergio Barbero
  • 181
  • 1
  • 2
  • 8

3 Answers3

3

A little more RxJS, but more elegant way:

getSumOfSpecificDayWeek(daysMonth: any, callback: any){
    var data = [];
    var that = this;
    let getCalls = []; // <--- This will contain all of your observables.
    daysMonth.forEach(function(day){
      const observable = that.statsService.getData(that.userid, day);
      getCalls.push(observable); // <--- Add the current observable to the array.
      observable.subscribe(async (res: any) => {
        data = JSON.parse(JSON.stringify(res));
        console.log(that.data);
        that.data = that.data.map( function(v, i) {
          return v + data[i];
        });
      });
    });
    // And here, you can use `callback`:
    Observable.forkJoin(...getCalls).subscribe(results => {
      callback("this should be at the end");
    });
}
Roland Rácz
  • 2,879
  • 3
  • 18
  • 44
  • This is the way to go. Even though I recommend assigning that.statsService.getData(that.userid, day) to a variable, and pushing this variable in the array and call subscribe on it. It looks better than getCalls$[getCalls$.length - 1] – Nicolas Gehlert Mar 29 '18 at 14:46
  • Also is there a reason/guideline for the $ after the variable name? Never actually so this and i'm just curious :D – Nicolas Gehlert Mar 29 '18 at 14:46
  • @NicolasGehlert You are absolutly right, I edit my answer. :) – Roland Rácz Mar 29 '18 at 14:47
  • 1
    There is no official guide about $, but a lot of people use this to indicate an Observable variable. (https://stackoverflow.com/questions/37671700/angular2-style-guide-property-with-dollar-sign) Although this is an array of Observables, so maybe this isn't necessary anyway. – Roland Rácz Mar 29 '18 at 14:53
  • wont this call the api twice as you are subscribing twice once inside the foreach method and once during forkjoin. – Shantam Mittal May 01 '19 at 19:50
0

So, this kind of thing is sadly a little confusing in Javascript. What this will do is to fire off a request for everything in daysMonth in the background, then call your callback. The async requests you fired off previously will then complete at some point.

Ultimately, what you need to do is to detect when you've done all the work, then fire off your callback. Take a look at something like this instead:

var numDone = 0;

daysMonth.forEach(function(day){
  that.statsService.getData(that.userid, day).subscribe(async (res: any) => {
    numDone++;

    data = JSON.parse(JSON.stringify(res));
    console.log(that.data);
    that.data = that.data.map( function(v, i) {
      return v + data[i];
    });

    if(numDone == daysMonth.length) {
      callback("All done!")
    }
  });
});

Effectively, we can do some work in the getData() callback, and if we're the last thing running, we then call the outer callback with any data we want.

Of course, this can get messy. The async library abstracts all this quite nicely, so you might be able to use async.map for your purposes too.

slugonamission
  • 9,562
  • 1
  • 34
  • 41
  • 2
    That said, I didn't notice you were already using RxJS. I'd definitely use @RolandRacz's answer instead. – slugonamission Mar 29 '18 at 14:51
  • You definitively should use @RolandRacz answer, since this version does not handle errors pretty well (and your callback won't be called). And in the Observable.forkJoin you can use an error handler. – Nicolas Gehlert Mar 29 '18 at 14:59
0

you need to manage the observable in another way

you need to execute a array of async operations and then execute a callback function

your code should look like

getSumOfSpecificDayWeek(daysMonth: any, callback: any){
    var data = [];
    var that = this;
    Observable.mergeArray(daysMonth.map(day=> that.statsService.getData(that.userid, day)).subscribe((arrOfResponses)=>{

// do your job with the data 
  callback("this should be at the end"); })


  }
Ricardo
  • 2,427
  • 19
  • 35