简体   繁体   English

异步 http 调用 Angular 后数据消失

[英]Data disappearing after async http calls in Angular

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.正如您可能猜到的,doors 指的是 buildingId,而 buildingID 指的是地区。 So to retrieve data and create my tree nodes, I have to do some http calls in forEach loops which is not asynchronous.因此,要检索数据并创建我的树节点,我必须在非异步的 forEach 循环中执行一些 http 调用。

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 上:

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".第一个 console.log(在 NgOnInit 中)返回一个空数组,这非常令人惊讶,因为第一个方法将数据放在“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.我想这与我使用的 async/await 有关。 Can anyone help?任何人都可以帮忙吗?

EDIT 1: Tried to use this.getDistricts().finally() instead of this.getDistricts().then(), but didn't work.编辑 1:尝试使用 this.getDistricts().finally() 而不是 this.getDistricts().then(),但没有用。

EDIT 2: console.log in getDistrict get executed after the one before my loop.编辑2:getDistrict 中的console.log 在我的循环之前的那个之后执行。 The expected behavior would be the opposite.预期的行为将是相反的。

SOLVED: putting the for loop in a finally block after my HTTP call solves this.已解决:在我的 HTTP 调用后将 for 循环放在 finally 块中解决了这个问题。 So as the answer says, I think I'm over engineering the async/await calls.所以正如答案所说,我认为我已经过度设计了 async/await 调用。 I have to rethink my work based on this.我必须在此基础上重新思考我的工作。 Thank you everyone!谢谢大家!

Well, you should return your Promise from getDistricts .好吧,您应该从getDistricts Promise Also you are very much over engineering and complicating the async/await concept.此外,您对async/await概念的工程和复杂化非常过度。 I understand you don't want to use Observables , but I would advise you to use them anyways.我知道您不想使用Observables ,但我还是建议您使用它们。

With promises and async/await so you kinda see how to use them:有了 Promise 和async/await ,你就知道如何使用它们了:

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:使用Observable ,它看起来像这样:

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

Just to provide whoever needs to make multiple http calls in a desired order.只是为需要按所需顺序进行多次 http 调用的人提供。 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 () => {... }).诀窍是使用可观察对象,使用.toPromise() 将它们转换为 Promise,使用.then() 将数据获取到我的变量中,然后在 finally 块中使用.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.祝你今天过得愉快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM