Как ждать дополнительного наблюдаемого - PullRequest
0 голосов
/ 30 ноября 2018

В моем приложении 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);
      });
  }

Ответы [ 3 ]

0 голосов
/ 30 ноября 2018

Проблема заключается в том, что onSomeEvent выполняется синхронно, тогда как присвоение cancelProcessing выполняется асинхронно - позже.Я думаю, что вместо этого вы могли бы сделать что-то вроде следующего (RxJS 5):

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

    if (this.isNewUser()) {
        this.userService.duplicateUserSearch(contact.email, contact.userName)
            .filter((result) => result.result === '')
            .switchMap(() => this.userService.updateContact(contact))
            .subscribe((res) => this.activeModal.close(res));
    } else {
        this.userService.updateContact(contact)
            .subscribe((res) => this.activeModal.close(res));
    }
}

If предложение

Сначала наблюдаемый поток filter -edпервое условие.Это возвращает наблюдаемую, которая будет пересылать элементы из duplicateUserSearch наблюдаемой, когда result.result равен пустой строке.

Далее, передаваемые значения отбрасываются, как в вашем примере,и заменяется наблюдаемыми, возвращаемыми из updateContact, который вызывается по требованию только после того, как какое-то значение прошло фильтр, использованный ранее.

switchMap выравнивает входные значения, что означает, что, если они являются наблюдаемыми, они будут подписаны и их значения будут распространяться через наблюдаемое, возвращаемое switchMap, вместо наблюдаемогосами случаи.

Наконец, мы можем подписаться на поток, возвращаемый switchMap, и наблюдать значения, возвращаемые updateContact напрямую.

Иначе предложение

Если этоне новый пользователь, updateContact вызывается без предварительной проверки с помощью duplicateUserSearch.


. При использовании RxJS 6.x оператор pipe используется для указания последовательности операторов за один раз.:

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

    if (this.isNewUser()) {
        this.userService.duplicateUserSearch(
            contact.email,
            contact.userName,
        ).pipe(
            filter((result) => result.result === ''),
            switchMap(() => this.userService.updateContact(contact)),
        ).subscribe((res) => this.activeModal.close(res));
    } else {
        this.userService.updateContact(contact)
            .subscribe((res) => this.activeModal.close(res));
    }
}
0 голосов
/ 01 декабря 2018

Как вы показали в своем вопросе, если / то логика с наблюдаемыми может быть хитрой.Здесь есть хорошая дискуссия здесь .

У вас уже было несколько ответов, которые могли бы работать при определенных условиях, и я бы вылетел и оставил вас в умелых рукахФан или Джеффри, но я был заинтригован комментарием, который вы сделали к одному ответу: «Что произойдет, если я добавлю больше условий?».Это заинтересовало меня, и поэтому я хотел найти шаблон, который можно было бы заметить / сделать функциональным, понятным и легко расширяемым, если позже вы решите добавить дополнительные условия.Хороший вопрос заставляет меня задуматься, и вы, конечно, сделали это.:)

Я попытался применить шаблон, который был предложен в статье, которую я связал выше, к вашей проблеме, и код ниже - то, что я придумал.Как я упоминал в комментарии к вашему первоначальному вопросу выше, я не уверен на 100%, что вы намереваетесь обновить контакт, когда пользователь НЕ является новым пользователем, но сейчас я предположил, что вы это делаете.Отказ от ответственности: я не проводил строгих проверок, таких как создание Stackblitz и создание различных тестовых случаев, хотя именно так я обычно хотел бы ответить на такой вопрос, поэтому я приношу свои извинения за любые ошибки или непредвиденные последствия в приведенном ниже коде.Увы, выходной зовет.;)

В этом паттерне в статье предлагается создавать ветви для всех возможных путей через логику if / then.Я придумал две основные ветви, которые привели бы к вызову updateContact ().Обратите внимание, что с этим шаблоном легко добавить tap() в любую из ветвей, чтобы при необходимости выполнить дополнительную работу.Если я пропустил ветку, которую вы хотите добавить, ее также легко добавить.

Сердцем этого шаблона является merge () в конце.Это создает одну наблюдаемую область, которая объединяет две переданные. Если любая из них завершается, то подписка будет выполняться и запускать updateContact().В этом случае они никогда не будут выполнены оба из-за фильтра isNewUser(), гарантирующего, что будет активен только один, но если вы примените этот шаблон в другом месте, вы можете добавить take(1), если вам нужен только первый асинхронный, который 'wins '.

Я также показываю подписку и отписку, так как считаю, что это лучшая практика.

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();
}
0 голосов
/ 30 ноября 2018

Более функциональная мода.

onSomeEvent() {
  const contact: IContact = {
    ...this.userForm.value
  };
const updateContact=this.userService.updateContact(contact)
  .pipe(tap(res => {
    this.activeModal.close(res);
  }))

return of(this.isNewUser()).pipe(mergeMap(newUser=>{
   if(!newUser)
      return updateContact
   return this.userService.duplicateUserSearch(contact.email, contact.userName)
     .pipe(mergeMap(result=>result.result === ''?updateConcat:empty()))  
}))

}

// execute
this.onSomeEvent().subscribe()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...