【问题标题】:How can I set a variable from a promise/observable in Angular after the subscription has completed?订阅完成后,如何从 Angular 中的 promise/observable 设置变量?
【发布时间】:2019-03-22 09:42:12
【问题描述】:

我正在打电话给 PokeApi 以获取口袋妖怪列表。然后我需要进行第二次调用以返回每个口袋妖怪的精灵图像。

我能够使用 rxjs Observables 正确返回 Pokemon 列表。当这返回口袋妖怪名称时,我再次调用口袋妖怪详细信息以返回精灵。然而,第二次调用没有及时完成以返回 Pokemon Sprites。

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 和 shinySprite。

编辑 下面是 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;
  })
)

}

【问题讨论】:

  • 我不确定您要做什么。如果您查看英雄之旅,他们会非常广泛地涵盖这一点。可能是这个问题可以澄清的情况,但你不应该对 rxjs 做任何疯狂的事情来让它正常工作。

标签: angular promise rxjs observable


【解决方案1】:

这是因为promise.then() 执行了异步操作。因此,当您返回new Pokemon() 时,可能不会始终设置精灵变量。所以你需要等待异步操作完成。

因此,对于第二个映射,您可以使用 switchMaprxjs 来代替 map 来订阅内部可观察对象/链可观察对象。在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(我命名它是为了展示如何使用)方法时,它会返回一个链式的observable。当你订阅时,它会返回一个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

【讨论】:

  • 不幸的是,这不起作用,因为从未调用 spriteObservable 将其推入 listOfObservables
  • 我想我找到了问题所在。在switchMap 中,我将结果命名为pokemonList,但使用_.map(),可迭代对象命名为pokemon。我编辑了这个问题。请看一下。
  • 对不起,我已经接受了这个并进行了更改,恐怕仍然无法正常工作
  • 为了更清楚,我添加了 getPokemonSprites 方法来提问
  • 您能创建一个 stackblitz 项目以便我进一步帮助您吗?
猜你喜欢
  • 2020-03-18
  • 2019-02-07
  • 2019-08-16
  • 2017-11-30
  • 1970-01-01
  • 2017-12-28
  • 2018-09-05
  • 2021-11-16
  • 1970-01-01
相关资源
最近更新 更多