简体   繁体   English

Angular 异步函数 OnInit

[英]Angular Async functions OnInit

I'm having this issue on handling async calls on the ngOninit().我在处理 ngOninit() 上的异步调用时遇到了这个问题。 Basically I need to load a few things sequentially but I don't know how.基本上我需要按顺序加载一些东西,但我不知道如何。

Here's the code:这是代码:

    import { Component, OnInit } from '@angular/core';
    import { Match, Game, participantIdentities, player } from '../../models/match';
    import { MatchService } from '../../services/match.service';
    import { Global } from '../../services/global';
    import { Observable } from 'rxjs';

    @Component({
      selector: 'app-players',
      templateUrl: './players.component.html',
      styleUrls: ['./players.component.css'],
      providers: [MatchService]
    })
    export class PlayersComponent implements OnInit {
      public matchs: Array<Match>;
      public games: Array<Game>
      public player: Array<player>;
      public players: []
      constructor(
        private _matchService: MatchService
      ) { 
        this.games = []
        this.player = []
      }

      ngOnInit(): void {
        this.getMatchs()
      }

      getMatchs(){
          this._matchService.getMatchs().subscribe(
            response =>{
              
              this.matchs = response.matchs;
              console.log(this.matchs);
              for(let i = 0; i<this.matchs.length; i++){
                this.games[i] = this.matchs[i].game;
              };
              console.log(this.games)
            }
            ,error=>{
              console.log(<any>error)
            }
          );
        }
      getUsers(){
          let accountId = [];
          for(let i = 0; i < this.matchs.length; i++){
            for(let x = 0; x < this.matchs[i].game.participantIdentities.length; x++){
              if ( typeof(accountId.find(element=>element == this.matchs[i].game.participantIdentities[x].player.summonerName)) === "undefined"){
                accountId.push(this.matchs[i].game.participantIdentities[x].player.summonerName)
                this.player.push(this.matchs[i].game.participantIdentities[x].player)
              }
            } 
          }
          console.log(this.player)
        }
    }

So as you can see, the data that getUsers() function is using, is coming from getMatchs().如您所见,getUsers() function 使用的数据来自 getMatchs()。 if I execute both functions on ngOninit getUsers() it will throw an error, because the other one has not been done.如果我在 ngOninit getUsers() 上执行这两个函数,它会抛出一个错误,因为另一个还没有完成。 Which makes sense.这是有道理的。 I could, of course, incorporate getUsers() into a button, but is not really my intention.当然,我可以将 getUsers() 合并到一个按钮中,但这并不是我的真正意图。 I had this issue before and I would love resolve it properly.我以前遇到过这个问题,我很想妥善解决它。 So the question is, how do I wait for it's completion in order to run the next function.所以问题是,我如何等待它完成才能运行下一个 function。

One way is to place getUsers() right after this.matchs = response.matchs;一种方法是在this.matchs = response.matchs;之后放置getUsers() , then every time you call getMatchs it calls getUsers too. ,然后每次调用getMatchs时,它也会调用getUsers

another way is to move subscription to ngOnInit.另一种方法是将订阅移至 ngOnInit。


  ngOnInit(): void {
    this.getMatchs().subscribe(response => {
      this.matchs = response.matchs;
      for(let i = 0; i<this.matchs.length; i++){
        this.games[i] = this.matchs[i].game;
      };
      this.getUsers();
    });
  }

getMatchs(){
      return this._matchService.getMatchs();
    }

  getUsers(){
      let accountId = [];
      for(let i = 0; i < this.matchs.length; i++){
        for(let x = 0; x < this.matchs[i].game.participantIdentities.length; x++){
          if ( typeof(accountId.find(element=>element == this.matchs[i].game.participantIdentities[x].player.summonerName)) === "undefined"){
            accountId.push(this.matchs[i].game.participantIdentities[x].player.summonerName)
            this.player.push(this.matchs[i].game.participantIdentities[x].player)
          }
        } 
      }
      console.log(this.player)
    }

@satanTime's solution is fine. @satanTime 的解决方案很好。
However, I'd like to offer another solution in case you don't want to missplace the getUsers() call.但是,如果您不想错过getUsers()调用,我想提供另一种解决方案。

You can try using Promises .您可以尝试使用Promises

getMatchs(): Promise<void> {
    return new Promise((resolve, reject) => {
        this._matchService.getMatchs().subscribe(
            response => {
                // Do whatever you need
                resolve();
            },
            error => {
                // Handle error
                // reject(); if you want to scale the exception one level upwards.
            }
        );
    });
}

Then, rewrite your ngOnInit method like so然后,像这样重写您的ngOnInit方法

ngOnInit(): void {
    this.getMatchs().then(() => {
        this.getUsers();
    });
}

Which becomes quite a bit more readable.这变得更具可读性。

Get matchs .获取匹配项 Once you are done, get the users.完成后,获取用户。

Just for completion and fanciness, you can cast the Observable returned on _matchService.getMatchs() to a Promise , work on it, then return it.只是为了完成和幻想,您可以将_matchService.getMatchs()返回的Observable转换为Promise ,处理它,然后返回它。

getMatchs = (): Promise<void> => 
    this._matchService.getMatchs().toPromise()
        .then(response => {
            // Do whatever you need
        })
        .catch(err => {
            // Handle error
        });

Hope it helps.希望能帮助到你。

as you can see there are a few different solutions to your problem.如您所见,您的问题有几种不同的解决方案。 My advice is to keep using the observable chain and wait until you getMatchs finishes.我的建议是继续使用可观察链并等到 getMatchs 完成。 For example:例如:

ngOnInit(): void {
  this.getMatchs().subscribe(res => this.getUsers());
}

then you would have to change your getmatchs functions like this:那么您将不得不像这样更改您的 getmatchs 函数:

getMatchs() {
  this._matchService.getMatchs().pipe(
    tap(result => {
      this.matchs = response.matchs;
      this.games = this.matchs.map(m => m.game);
    })
  );  

By doing this you keep using your observable stream.通过这样做,您可以继续使用可观察的 stream。

That should work but there are other issues that you should be aware of.这应该可行,但您应该注意其他问题。 One of them is that it is a very good practice to unsubscribe from every subscription you want to avoid memory leaks in your app.其中之一是取消订阅您想要避免应用程序中的 memory 泄漏的每个订阅是一个非常好的做法。 You can do this by calling unsubscribe() manually or rely on the async pipe from angular.您可以通过手动调用unsubscribe()或依赖来自 angular 的async pipe 来执行此操作。

https://rxjs-dev.firebaseapp.com/guide/subscription https://rxjs-dev.firebaseapp.com/guide/subscription

https://angular.io/api/common/AsyncPipe https://angular.io/api/common/AsyncPipe

On the other hand you would get better performance if you could modify your code to reduce the amount of loops that you do over the same info.另一方面,如果您可以修改代码以减少对相同信息执行的循环数量,您将获得更好的性能。 Check out this other version of GetMatchs I was doing for you (not tested) but hopefully can give you an idea of how to improve the performance of your component:查看我为您做的另一个版本的 GetMatchs(未测试),但希望可以让您了解如何提高组件的性能:

processMatches() {
   this._matchService.getMatches().pipe(
      tap(response => {
         this.matches = response.matchs;
         let accountId = {};            

         this.matches.forEach((m, index) => {
            this.game[index] = m[index].game;
            this.game[index].participantIdentities
                .forEach(p => {
                    if (!accountId[p.player.sumonerName]) {
                        accountId[p.player.sumonerName] = p.player.sumonerName;
                        this.player.push(p.player);
                    }
                });      
             });

         })
     )
 }

Again, this code is not tested by the idea here is to reduce the loops and also converted the accountId array to an object in order to check for duplicates easier and faster同样,此代码未经过测试,这里的想法是减少循环并将 accountId 数组转换为 object 以便更轻松、更快地检查重复项

Happy coding!快乐编码!

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

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