如何等待可选的 Observable

Joe*_*Joe 3 rxjs typescript angular

在我的 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);
      });
  }
Run Code Online (Sandbox Code Playgroud)

dmc*_*dle 5

正如您在问题中所展示的,如果/那么 Observables 的逻辑可能很棘手。这里有一个关于这个的很好的讨论。

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

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

在本文的模式中,建议通过 if/then 逻辑为所有可能的路径创建分支。我提出了两个主要分支,这两个分支将导致 updateContact() 被调用。请注意,使用此模式可以轻松地tap()在任何分支中添加 a 以在需要时执行其他工作。如果我错过了您想要添加的分支,也应该很容易添加。

这个模式的核心是最后的 merge() 。这将创建一个合并传入的两个可观察量的单个可观察量。如果其中任何一个完成,则订阅将执行并运行updateContact()。在这种情况下,它们永远不会同时完成,因为过滤器保证isNewUser()只有一个会处于活动状态,但如果您在其他地方应用此模式,如果您只关心第一个“获胜”的异步模式,则可能需要添加 a 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();
}
Run Code Online (Sandbox Code Playgroud)