【问题标题】:Angular 2 calling multiple async methodsAngular 2调用多个异步方法
【发布时间】:2017-08-08 01:21:12
【问题描述】:

我正在构建一个移动应用程序,现在我正在处理身份验证。在我点击我的主页之前,我需要在我构建的 API 上点击各种端点,然后才能向用户显示数据。

在 Postman 中测试时,所有端点都返回了正确的数据,但是当我在我的应用程序中使用它时,我在第二次异步调用中得到了一个 null 值。

我确信这与进行这些调用的顺序有关,因此我只是在寻求一些帮助,以了解如何在开始另一个调用之前正确等待一个调用完成。

public login() {

this.showLoading();

this.userService.getUserIdFromUserName(this.registerCredentials.username) // WORKS
  .subscribe(
    res => {
      console.log(res);
      localStorage.setItem("UserId", res.toString());
    },
    err => {
      console.log(err);
    });

this.userService.getEmployeeIdFromUserId(localStorage.getItem("UserId")) // THIS RETURNS NULL 
  .subscribe(
    res => {
      console.log(res);
      localStorage.setItem("EmployeeId", res.toString());
    },
    err => {
      console.log(err);
    });

this.authService.login(this.registerCredentials)
  .subscribe(

    data => {
      this.loading.dismissAll();
      console.log('User logged in successfully! ', data);
      this.nav.push(TabsPage);
      localStorage.setItem("Username", this.registerCredentials.username);
      localStorage.setItem("isLoggedIn", "true");
    },

    error => {
      this.loading.dismissAll();
      this.showAlert("Uh oh!", "Something went wrong. Please re-enter your login credentials or check your connection.");
      console.log(error);
    });
  }

【问题讨论】:

  • 标题为Angular2,但关键字为angularjs。您可以考虑修改两者之一。
  • 你应该在this answer找到你需要的东西。

标签: javascript angular typescript rxjs


【解决方案1】:

您的原始代码存在导致此错误的错误。您的代码中有三个调用,我将它们称为 A)、B) 和 C):

A) this.userService.getUserIdFromUserName(this.registerCredentials.username) // WORKS

B) this.userService.getEmployeeIdFromUserId(localStorage.getItem("UserId")) // THIS RETURNS NULL 

C) this.authService.login(this.registerCredentials)

关于 RXJS,您需要了解的是 cold Observable(代表启动异步操作所需的所有信息)和 hot Observable(这是一个异步操作已经开始的 Observable)。

这三个调用 A)、B) 和 C) 只是构建冷的 observables,它们在你调用 .subscribe() 的那一刻就开始了。因此,当 B) 建成时,A) 已经开始但尚未完成。因此对localStorage.getItem("UserId") 的调用将返回null,因为A) 尚未调用其订阅者的next 回调。

所以你想要做的是让 B) 等待 A)。此外,与其将某些东西填充到全局状态(localStorage)中,不如将结果从 A)流向 B)。输入.mergeMap() 运算符:

this.userService.getUserIdFromUserName(this.registerCredentials.username) // WORKS
  .map(res => res.toString())
  .do(userId => localStorage.setItem("UserId", userId)) // cleanly separate side-effects into .do() calls
  .mergeMap(userId => this.userService.getEmployeeIdFromUserId(userId))
  .map(res => res.toString())
  .do(employeeId => localStorage.setItem("EmployeeId", employeeId))
  .subscribe(
    employeeId => {
      console.log(employeeId);      
    },
    err => {
      console.log(err);
    });

关于 rxjs 的好处是错误处理可以一直通过 Observable 链进行。 如果您可以并行执行 C),请查看 .forkJoin().

最后,如果您需要对 .mergeMap() 的解释,请查看以下答案:SwitchMap vs MergeMap in the #ngrx example

【讨论】:

  • +1:很好的答案。这里真的很容易理解并且整体非常干净的代码。显然这与你无关,但我希望 RxJS 操作符有更好的名字。人们想摆脱selectMany(这是一个好名字),所以他们选择了flatMap(这也是一个好名字)。两者都保留了与集合的类比。 mergeMap 什么都不做。它可能在排序方面更具描述性,或者缺少它们,但读起来像是故意提供直觉,但它却惨遭失败。他们不妨将其重命名为 bind 并完成它。
  • 问题:switchmap 不是更好的解决方案吗? (我知道区别,但仍然......)
【解决方案2】:

这应该有效。不要忘记import 'rxjs/Rx'

this.userService.getUserIdFromUserName(this.registerCredentials.username)
  .map(res => res.toString())
  .do(userId => {
      console.log(res);
      localStorage.setItem("UserId", userId);
    })
  .flatMap(userId => {
       return this.userService.getEmployeeIdFromUserId(userId);
     })
  .do(res => {
      console.log(res);
      localStorage.setItem("EmployeeId", res.toString());
    })

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-11
    相关资源
    最近更新 更多