Как я могу установить переменную из обещания / наблюдаемого в Angular после завершения подписки? - PullRequest
0 голосов
/ 22 марта 2019

Я звоню в PokeApi, чтобы получить список Pokemon.Затем мне нужно сделать второй вызов, чтобы вернуть изображения спрайтов для каждого покемона.

Я могу правильно вернуть список покемонов, используя rxjs Observables.Когда это возвращает имя покемона, я делаю второй вызов детали покемона, чтобы вернуть спрайтов.Однако второй вызов не завершается вовремя, чтобы вернуть спрайтов покемонов.

public getListOfPokemon(page: number): Observable<Pokemon[]> {
let apiUrl = `${this.apiBaseUrl}/pokemon/?limit=1`;

if (page > 0) {
  let offset = page * 42;

  apiUrl = `${this.apiBaseUrl}/pokemon/?offset=${offset}&limit=42`;
}

return this._httpClient.get(apiUrl).pipe( 
  map((resp: any) => {
    if (resp.results.length == 0) {
      this.lastPageOfResults = resp.next == null;
      this.router.navigate(['/404']);
    }

    this.lastPageOfResults = resp.next == null;

    let pkmnList = _.map(resp.results, (pkmn) => {
      let frontSprite: string;
      let shinySprite: string;

      new Promise(resolve => {
        resolve(this.getPokemonSprites(pkmn.name).subscribe(sprite => {
          return sprite;
        }))
      }).then((value) => {
        console.log(value);
      });

      let singlePkmn = new Pokemon(pkmn.name, frontSprite, shinySprite);

      return singlePkmn;
    });

    return pkmnList;
  })
)

}

frontSprite и глянцевыйSprite не устанавливаются, когда мы возвращаем нового покемона.

РЕДАКТИРОВАТЬ Вот метод getPokemonSprites для получения дополнительной информации

private getPokemonSprites(name: string): Observable<any> {
let apiUrl = `${this.apiBaseUrl}/pokemon/${name}`;   

return this._httpClient.get(apiUrl).pipe(
  map((pkmnDetail: any) => {
    console.log('hi');
    return pkmnDetail.sprites;
  })
)

}

1 Ответ

0 голосов
/ 22 марта 2019

Это происходит потому, что promise.then() выполняет асинхронную операцию. Поэтому, когда вы возвращаете new Pokemon(), переменные спрайта не всегда могут быть установлены. Поэтому вам нужно дождаться завершения асинхронных операций.

Итак, для второго отображения вместо map вы можете использовать switchMap из rxjs, чтобы подписаться на внутреннюю наблюдаемую / цепочку наблюдаемых. А в switchMap вы можете объединить массив наблюдаемых с combineLatest, чтобы получить ответы наблюдаемых в виде массива. Как ниже:

getPokemons = () => {

    return this._httpClient.get(apiUrl).pipe( 
      map((resp: any) => {
        if (resp.results.length == 0) {
          this.lastPageOfResults = resp.next == null;
          this.router.navigate(['/404']);
        }

        this.lastPageOfResults = resp.next == null;

        return resp.results;
      }),

      switchMap((pokemonList) => {

        const listOfObservables = [];

        _.map(pokemonList, (pkmn) => {
          let frontSprite: string;
          let shinySprite: string;

          const spriteObservable = this.getPokemonSprites(pkmn.name).pipe(
            map((sprite) => {
               let frontSprite = sprite.front_default;
               let shinySprite = sprite.front_shiny;  
               return new Pokemon(pkmn.name, frontSprite, shinySprite);   
            })
          )

          listOfObservables.push(spriteObservable);

        });

        return combineLatest(listOfObservables);
      })
    )
}

Затем, когда вы вызываете метод getPokemons (я назвал его, чтобы показать, как использовать), он возвращает цепочку наблюдаемой. Когда вы подписываетесь, он возвращает массив Pokemon:

getPokemons.subscribe((pokemonList: Array<Pokemon>) => {
    // do the fancy stuff with pokemonList array
})

Надеюсь, это поможет.


Рекомендации:

объединитьПоследние: https://www.learnrxjs.io/operators/combination/combinelatest.html

switchMap: https://www.learnrxjs.io/operators/transformation/switchmap.html

...