1

I have to call some methods synchronically in ngOnInit() method, but all of them make some asynchronous calls to an API from the inside. Some variables are setted in the first method, and the second and the third methods need these variables to be already setted. I can't use rxjs mapping operators (mergeMap, switchMap, concatMap, exhaustMap) since the calls are made from separated methods that make other operations.

This is what I have:

public taskTypes: any; // variable to be setted in first method
public firstDataSource: any;
public secondDataSource: any;
// some other variables to be setted in first method

constructor(...) {
  this.taskTypes = [];
  this.firstDataSource = [];
  this.secondDataSource = [];
  // some other initializations
}

ngOnInit() {
  this.getTaskTypes();
  this.methodOne();
  this.methodTwo()
}

public getTaskTypes(): void {
  this.http.get("url").subscribe(
    (res) => {
      this.taskTypes = res.tasks;
      // some other operations with response data
      // and set other variables
    },
    (err) => {}
  );
  // some other operations with variables
}

public methodOne(): void {
  if (this.taskTypes.length > 0) {
    this.http.get("url/" + 1).subscribe(
      (res) => {
        this.firstDataSource = res.data;
      },
      (err) => {}
    );
  }
  // some other operations with variables
}

public methodTwo(): void {
  if (this.taskTypes.length > 0) {
    this.http.get("url/" + 2).subscribe(
      (res) => {
        this.secondDataSource = res.data;
      },
      (err) => {}
    );
  }
  // some other operations with variables
}

My problem is that with the call of ngOnInit(), all inner methods are called at the same time and the result of the condition: this.taskTypes.length > 0 is always false because the variable taskTypes has not been setted by the time when methodOne() and methodTwo() are called, so firstDataSource and secondDataSource never get setted with the respective calls to the API. That's why I need methodOne() and methodTwo() to be called synchronously after getTaskTypes().

nasoram
  • 11
  • 1
  • 4
  • 1
    this just looks like poorly structured code. why can't whatever `getTasksTypes()` or `methodOne()` etc doing besides the http call be separated out into other functions that get called independently. when youre working with asynchronous functions, structuring your functions correctly is essential to maintaining a good code base. – bryan60 Nov 27 '19 at 01:38

3 Answers3

1

Of course you CAN do that with rxjs

this.http.get("url").pipe(
    map(res => res.tasks),
    filter(taskTypes => taskTypes.length > 0),
    switchMap(taskTypes => combineLatest([this.http.get("url/" + 1), this.http.get("url/" + 2)])),
  ).subscribe(
   ([methodOneResult, methodTwoResult]) => console.log(methodOneResult, methodTwoResult),
  );

Have an example here: https://stackblitz.com/edit/angular-jevauk

MoxxiManagarm
  • 8,735
  • 3
  • 14
  • 43
0

First change your methods to return observable only e.g

public getTaskTypes():Observable<any> {
 return this.http.get("url");
}

Then use concat to execute them sequentially, and toArray() to convert result to an array

 concat(this.getTaskTypes(),this.methodOne(),this.methodTwo())
 .pipe(toArray()).subscribe(res=>console.log(res))

res will contain all results in a array in execution order.

Fan Cheung
  • 10,745
  • 3
  • 17
  • 39
  • I'll give it a try, but what about the other operations that are made in the methods? The methods call other services, show some alerts... – nasoram Nov 27 '19 at 04:41
  • you can chain it to each individual method using pipe() and other operators. e.g this.getTaskTypes().pipe(tap(_=>alert("hello"))) – Fan Cheung Nov 27 '19 at 08:45
  • I couldn't accomplish all what I wanted to do, but this serves the purpose of synchronous calls – nasoram Dec 03 '19 at 14:32
-1

You should use the concept of Promise and Then, or async await. For example, your ngOnInit should be like this:

async ngOnInit() {
  await this.getTaskTypes();
  await this.methodOne();
  await this.methodTwo()
}

By using await, methodOne will wait until getTaskTypes be resolved. But keep that in mind, await only works if your function returns a Promise or an Observable. In this case, because your functions do not return anything, await won't work. So, for example getTaskTypes should be like this:

public async getTaskTypes(){
  return new Promise(resolve, reject) => {
      this.http.get("url").subscribe(
      (res) => {
        this.taskTypes = res.tasks;
        resolve();
        // some other operations with response data
        // and set other variables
      },
      (err) => {
        reject(err);
      }
    );
    // some other operations with variables
  });
}

Of course there are better approaches to solve this, but I tried to make you understand the main ideas. You can read more about it in here.

  • _"Should"_ use? `Observables` are perfectly fine to use for this purpose! – Daniel B Nov 27 '19 at 13:20
  • I said "there are better approaches to solve this", but still there is nothing wrong with Promises. You are free to use anything you want. I suggest you to read [https://stackoverflow.com/questions/37364973/what-is-the-difference-between-promises-and-observables] – Puria Rad Jahanbani Nov 27 '19 at 14:03
  • I beg to disagree. You state that the OP _"Should"_ use something entirely different than what the question is about, and the question doesn't mention the word `Promise` anywhere. The word you are looking for is _"Could"_, not _"Should"_. – Daniel B Nov 28 '19 at 09:06
  • 1
    According to [this](http://www.differencebetween.net/language/grammar-language/difference-between-should-and-must/), “Should” is used to denote recommendations, advice, or to talk about what is generally right or wrong within the permissible limits of society. – Puria Rad Jahanbani Nov 28 '19 at 09:18