首页 > 解决方案 > 如何等待一个可选的 Observable

问题描述

在我的 Angular 应用程序中,我进行了可选的 HTTP 调用来检查用户是否存在。根据这次调用的结果,我要么想打另一个电话,要么停止处理。如果不诉诸.toPromise(),我如何等待在一个if块中进行的呼叫?

  onSomeEvent() {
    const contact: IContact = {
      ...this.userForm.value
    };

    let cancelProcessing = false;

    if (this.isNewUser()) {
      // *** This is optional
      this.userService.duplicateUserSearch(contact.email, contact.userName)
        .subscribe(result => {
          if (result.result !== '') {
            cancelProcessing = true;
          }
        });
    }

    // *** This statement is hit before the observable completes
    if (cancelProcessing) {
      return;
    }

    //  *** which means that this runs when it shouldn't
    this.userService.updateContact(contact)
      .subscribe(res => {
        this.activeModal.close(res);
      });
  }

标签: angulartypescriptrxjs

解决方案


正如您在问题中展示的那样,使用 Observables 的 If/Then 逻辑可能很棘手。这里有一个很好的讨论。

你已经有一些在特定条件下可以工作的答案,我会冲浪离开,把你交给范或杰弗里有能力的人,但我对你对一个答案的评论很感兴趣:“如果我添加更多条件?”。这引起了我的兴趣,因此我想找到一种基于可观察/基于功能的模式,如果您稍后决定添加其他条件,则易于阅读且易于扩展。一个好问题让我很难思考,而你确实做到了。:)

我尝试将上面链接的文章中建议的模式应用于您的问题,下面的代码就是我想出的。正如我在上面对您原来的问题的评论中提到的,我不是 100% 确定您打算在用户不是新用户时更新联系人,但现在我假设您这样做了。免责声明:我没有严格测试这一点,例如创建 Stackblitz 和创建各种测试用例,尽管这是我通常想要回答这样的问题的方式,所以对于以下代码中的任何错误或意外后果,我深表歉意。唉,周末来了。;)

在文章的该模式中,建议为通过 if/then 逻辑的所有可能路径创建分支。我想出了两个会导致调用 updateContact() 的主要分支。请注意,使用此模式很容易tap()在任何分支中添加 a 以在需要时进行额外的工作。如果我错过了你想添加的分支,那也应该很容易添加。

这种模式的核心是最后的 merge()。这将创建一个合并两个传入的可观察对象。如果其中任何一个完成,则订阅将执行并运行updateContact()。在这种情况下,它们永远不会都完成,因为过滤器保证只有一个是活动的,但是如果你在其他地方应用这个模式,如果你只关心第一个“获胜”的异步isNewUser()模式,你可能想要添加一个。take(1)

我还显示订阅和取消订阅,因为我认为这是最佳做法。

onSomeEventSub : Subscription; // component scope variable to add to later unsubscribe with

onSomeEvent() {
    const contact: IContact = {
      ...this.userForm.value
    };

    // define duplicateUserSearch$ now for easier readability below
    const duplicateUserSearch$: Observable<boolean> = 
        this.userService.duplicateUserSearch(contact.email, contact.userName).pipe(
            map(result => (result.result === '')));

    // First, create a shareable source for eventual merge of all branches.
    // of(0) is completely arbitrary.  It would be more interesting to tie
    // this to the original event, but that wasn't part of the question.  :)
    const source$ = of(0).pipe(
        share() // same observable will have multiple observers
    );

    // branch 1 is for when the user is a new user and duplicate logic asserts
    const isNewUserAndIsDupe$ = source$.pipe(
        filter(() => isNewUser()),
        mergeMap(() => duplicateUserSearch$),
        filter(res => res)
    );

    // branch 2 is when the user is NOT a new user
    const isNotNewUser$ = source$.pipe(
        filter(() => !isNewUser())
    );

    // and finally the merge that pulls this all together and subscribes
    this.onSomeEventSub = merge(isNewUserAndIsDupe$, isNotNewUser$).pipe(
        mergeMap(() => this.userService.updateContact(contact))
    ).subscribe(res => this.activeModal.close(res));

}

ngOnDestroy() {
    if (this.onSomeEventSub) this.onSomeEventSub.unsubscribe();
}

推荐阅读