0

I have to code a tree component that displays multiple data, which worked fine with mocked data for me. The problem here is when I try to get data from servers, let me explain:

I have three main objects : Districts, buildings and doors. As you may guess, doors refers to buildingId and buildingID to districts. So to retrieve data and create my tree nodes, I have to do some http calls in forEach loops which is not asynchronous.

I won't share with you everything but just a minimized problem so I can get help easily:

This method retrieves a district array from the server and puts it in a local array:

async getDistricts(){
   this.districtDataService.getDistrictData().toPromise().then(async districts => {
     this.districts = await districts.results as District[];
   });

}

On my ngOnInit :

ngOnInit() {
this.getDistricts().then(async () => {
  console.log(this.districts);
  for (const district of this.districts){
    console.log(district);
  }
})

The first console.log (in NgOnInit) returns an empty array, which is quite surprising because the first method puts the data in "this.districts". and logging data in the first method just after I put it in returns an array with my data. I guess it have something to do with the async/await I've used. Can anyone help?

EDIT 1: Tried to use this.getDistricts().finally() instead of this.getDistricts().then(), but didn't work.

EDIT 2: console.log in getDistrict get executed after the one before my loop. The expected behavior would be the opposite.

SOLVED: putting the for loop in a finally block after my HTTP call solves this. So as the answer says, I think I'm over engineering the async/await calls. I have to rethink my work based on this. Thank you everyone!

8
  • 1
    Try to put a log statement into the getDistricts, after you awaited districts.results, and log this.districts. Also, IMHO this whole thing would be easier to read if you would stick to rxjs. Commented Aug 5, 2020 at 9:44
  • It still doesn't work. i tried putting two console.log (one in getDistricts and another one before my for loop). actually the one in the forloop get executed before the one in getDistrict. I think that is the problem Commented Aug 5, 2020 at 9:49
  • do some http calls in forEach loops wich is not asynchronous - how could multiple HTTP calls be synchronous? HTTP calls are by behavior always asynchronous. Commented Aug 5, 2020 at 9:53
  • When having a lot of them, the order of execution is tricky to get right. At least in my case :/ Commented Aug 5, 2020 at 9:55
  • @AymaneELJahrani: Is the order of execution a concern for you? Commented Aug 5, 2020 at 9:56

2 Answers 2

2

Well, you should return your Promise from getDistricts. Also you are very much over engineering and complicating the async/await concept. I understand you don't want to use Observables, but I would advise you to use them anyways.

With promises and async/await so you kinda see how to use them:

async getDistricts(): Promise<District[]> {
  const { results } = await this.districtDataService.getDistrictData();
  
  return results;
}

async ngOnInit(): Promise<void> {
  this.districts = await this.getDistricts();

  for (const district of this.districts){
    console.log(district);
  }
}

With Observable it would look like this:

getDistricts(): Observable<District[]> {
  return this.districtDataService.getDistrictData().pipe(
    map(({ results }) => results as District[])
  );
}

ngOnInit(): void {
  this.getDistricts().subscribe((districts) => {
    this.districts = districts;
   
    for (const district of this.districts){
      console.log(district);
    } 
  });
}
Sign up to request clarification or add additional context in comments.

Comments

0

Just to provide whoever needs to make multiple http calls in a desired order. as mentionned by others, i overcomplicated the concept of async await. The trick was to use observables, convert them to Promises using .toPromise(), using .then() to get data into my variables, then making other async calls in finally block using .finally(async () => { ... }).

here's what my final code looks like :

async ngOnInit(): Promise<void> {
 await this.districtDataService.getDistrictData().toPromise().then(response => {
   this.districts = response.results as District[];
   console.log(this.districts);
 }).finally(async () => {
   for (const district of this.districts){
      await this.districtDataService.getBuildingsOfDistrict(district.id).toPromise().then(response => {
      this.buildings = response.results as Building[];
      console.log(this.buildings);
     }).finally(async ()=> {
        for(const building of this.buildings){
          await this.districtDataService.getDoorsOfBuilding(building.id).toPromise().then(response => {
            this.doors = response.results as Door[];
            console.log(this.doors);
          }).finally(async () => {
              for(const door of this.doors){
                  await this.doorNodes.push(new districtNodeImpl(false,null,null,door,null));
              }
          })
         await this.buildingNodes.push(new districtNodeImpl(false,null,building,null,this.doorNodes));
        }
     })
     await this.dataSource.push(new districtNodeImpl(false,district,null,null,this.buildingNodes));
     console.log(this.dataSource);
     this.buildingNodes = new Array();
     this.doorNodes = new Array();
   }
 })

Hope this will help ! have a nice day.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.