Sergio Barbero
Sergio Barbero

Reputation: 301

callback at the end of a loop in typescript

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);

Upvotes: 0

Views: 1182

Answers (3)

Roland Rácz
Roland Rácz

Reputation: 2999

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");
    });
}

Upvotes: 3

Ricardo
Ricardo

Reputation: 2487

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"); })


  }

Upvotes: 0

slugonamission
slugonamission

Reputation: 9642

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.

Upvotes: 0

Related Questions